Snap for 10768428 from fd84ccb43068313fcb8c96a94a1157d20015220b to mainline-conscrypt-release

Change-Id: If6dd999ea5a71b1c1f29600fc7ab448a44f3b654
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..b2bf99d 100644
--- a/.gn
+++ b/.gn
@@ -1,35 +1,33 @@
-# Copyright 2016 PDFium Authors. All rights reserved.
+# Copyright 2016 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-buildconfig = "//build/config/BUILDCONFIG.gn"
+# TODO(crbug.com/pdfium/1932): Switch back to //build/config/BUILDCONFIG.gn if
+# appropriate.
+buildconfig = "//build_overrides/BUILDCONFIG.gn"
+
+# The python interpreter to use by default. On Windows, this will look
+# for python3.exe and python3.bat.
+script_executable = "python3"
 
 default_args = {
-  v8_extra_library_files = []
-  v8_experimental_extra_library_files = []
+  # PDFs only need to run JavaScript.
+  v8_enable_webassembly = false
 
   # Turns on compiler optimizations in V8 in Debug build.
   v8_optimized_debug = true
+
+  # PDFium is currently incompatible with the V8 Sandbox.
+  # See https://crbug.com/v8/13014 for details.
+  v8_enable_sandbox = false
 }
 
-check_targets = [
-  ":pdfium",
-  ":pdfium_embeddertests",
-  ":pdfium_unittests",
-  "//constants/*",
-  "//core/*",
-  "//fpdfsdk/*",
-  "//fxbarcode/*",
-  "//fxjs/*",
-  "//samples/*",
-  "//skia/*",
-  "//testing/:*",
-  "//testing/fuzzers/*",
-  "//testing/image_diff/*",
-  "//third_party:bigint",
-  "//third_party:fx_agg",
-  "//third_party:fx_freetype",
-  "//third_party:pdfium_base",
-  "//third_party:skia_shared",
-  "//xfa/*",
+no_check_targets = [
+  # See https://crbug.com/v8/7330 and/or check if these entries exist in
+  # Chromium's //.gn file.
+  "//v8:cppgc_base",
+  "//v8:v8_internal_headers",
+  "//v8/src/inspector:inspector",
+  "//v8/test/cctest:cctest_sources",
+  "//v8/test/unittests:inspector_unittests_sources",
 ]
diff --git a/.style.yapf b/.style.yapf
new file mode 100644
index 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..b6cb1b5
--- /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-py2_py3"
+  version: "version:2.26.0"
+>
+wheel: <
+  name: "infra/python/wheels/urllib3-py2_py3"
+  version: "version:1.26.6"
+>
diff --git a/AUTHORS b/AUTHORS
index 3c97237..f8e3ed9 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -7,48 +7,39 @@
 #   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>
 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>
 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>
@@ -57,3 +48,5 @@
 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..cb1da47 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1,10 +1,17 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/clang/clang.gni")
+import("//build/config/gclient_args.gni")
 import("//testing/test.gni")
 import("pdfium.gni")
 
+group("default") {
+  testonly = true
+  deps = [ ":pdfium_all" ]
+}
+
 group("freetype_common") {
   public_deps = []
   if (pdf_bundle_freetype) {
@@ -16,12 +23,10 @@
 
 config("pdfium_common_config") {
   cflags = []
+  cflags_cc = []
   ldflags = []
   include_dirs = [ "." ]
-  defines = [
-    "PNG_PREFIX",
-    "PNG_USE_READ_MACROS",
-  ]
+  defines = []
 
   if (!use_system_libopenjpeg2) {
     defines += [ "OPJ_STATIC" ]
@@ -35,13 +40,208 @@
     defines += [ "_SKIA_SUPPORT_" ]
   }
 
-  if (pdf_use_skia_paths) {
-    defines += [ "_SKIA_SUPPORT_PATHS_" ]
+  if (pdf_use_partition_alloc) {
+    defines += [ "PDF_USE_PARTITION_ALLOC" ]
   }
 
   if (is_win) {
     # Assume UTF-8 by default to avoid code page dependencies.
     cflags += [ "/utf-8" ]
+
+    if (!is_clang) {
+      cflags += [
+        # Warnings permanently disabled:
+
+        # C4091: 'typedef ': ignored on left of 'X' when no variable is
+        #                    declared.
+        # This happens in a number of Windows headers. Dumb.
+        "/wd4091",
+
+        # C4127: conditional expression is constant
+        # This warning can in theory catch dead code and other problems, but
+        # triggers in far too many desirable cases where the conditional
+        # expression is either set by macros or corresponds some legitimate
+        # compile-time constant expression (due to constant template args,
+        # conditionals comparing the sizes of different types, etc.).  Some of
+        # these can be worked around, but it's not worth it.
+        "/wd4127",
+
+        # C4251: 'identifier' : class 'type' needs to have dll-interface to be
+        #        used by clients of class 'type2'
+        # This is necessary for the shared library build.
+        "/wd4251",
+
+        # C4275:  non dll-interface class used as base for dll-interface class
+        # This points out a potential (but rare) problem with referencing static
+        # fields of a non-exported base, through the base's non-exported inline
+        # functions, or directly. The warning is subtle enough that people just
+        # suppressed it when they saw it, so it's not worth it.
+        "/wd4275",
+
+        # C4312 is a VS 2015 64-bit warning for integer to larger pointer.
+        # TODO(brucedawson): fix warnings, crbug.com/554200
+        "/wd4312",
+
+        # C4324 warns when padding is added to fulfill alignas requirements,
+        # but can trigger in benign cases that are difficult to individually
+        # suppress.
+        "/wd4324",
+
+        # C4351: new behavior: elements of array 'array' will be default
+        #        initialized
+        # This is a silly "warning" that basically just alerts you that the
+        # compiler is going to actually follow the language spec like it's
+        # supposed to, instead of not following it like old buggy versions did.
+        # There's absolutely no reason to turn this on.
+        "/wd4351",
+
+        # C4355: 'this': used in base member initializer list
+        # It's commonly useful to pass |this| to objects in a class' initializer
+        # list.  While this warning can catch real bugs, most of the time the
+        # constructors in question don't attempt to call methods on the passed-in
+        # pointer (until later), and annotating every legit usage of this is
+        # simply more hassle than the warning is worth.
+        "/wd4355",
+
+        # C4503: 'identifier': decorated name length exceeded, name was
+        #        truncated
+        # This only means that some long error messages might have truncated
+        # identifiers in the presence of lots of templates.  It has no effect on
+        # program correctness and there's no real reason to waste time trying to
+        # prevent it.
+        "/wd4503",
+
+        # Warning C4589 says: "Constructor of abstract class ignores
+        # initializer for virtual base class." Disable this warning because it
+        # is flaky in VS 2015 RTM. It triggers on compiler generated
+        # copy-constructors in some cases.
+        "/wd4589",
+
+        # C4611: interaction between 'function' and C++ object destruction is
+        #        non-portable
+        # This warning is unavoidable when using e.g. setjmp/longjmp.  MSDN
+        # suggests using exceptions instead of setjmp/longjmp for C++, but
+        # Chromium code compiles without exception support.  We therefore have to
+        # use setjmp/longjmp for e.g. JPEG decode error handling, which means we
+        # have to turn off this warning (and be careful about how object
+        # destruction happens in such cases).
+        "/wd4611",
+
+        # Warnings to evaluate and possibly fix/reenable later:
+
+        "/wd4100",  # Unreferenced formal function parameter.
+        "/wd4121",  # Alignment of a member was sensitive to packing.
+        "/wd4244",  # Conversion: possible loss of data.
+        "/wd4505",  # Unreferenced local function has been removed.
+        "/wd4510",  # Default constructor could not be generated.
+        "/wd4512",  # Assignment operator could not be generated.
+        "/wd4610",  # Class can never be instantiated, constructor required.
+        "/wd4838",  # Narrowing conversion. Doesn't seem to be very useful.
+        "/wd4995",  # 'X': name was marked as #pragma deprecated
+        "/wd4996",  # Deprecated function warning.
+
+        # These are variable shadowing warnings that are new in VS2015. We
+        # should work through these at some point -- they may be removed from
+        # the RTM release in the /W4 set.
+        "/wd4456",
+        "/wd4457",
+        "/wd4458",
+        "/wd4459",
+
+        # All of our compilers support the extensions below.
+        "/wd4200",  # nonstandard extension used: zero-sized array in
+                    # struct/union
+        "/wd4201",  # nonstandard extension used: nameless struct/union
+        "/wd4204",  # nonstandard extension used : non-constant aggregate
+                    # initializer
+
+        "/wd4221",  # nonstandard extension used : 'identifier' : cannot be
+                    # initialized using address of automatic variable
+
+        # http://crbug.com/588506 - Conversion suppressions waiting on Clang
+        # -Wconversion.
+        "/wd4245",  # 'conversion' : conversion from 'type1' to 'type2',
+                    # signed/unsigned mismatch
+
+        "/wd4267",  # 'var' : conversion from 'size_t' to 'type', possible loss
+                    # of
+                    # data
+
+        "/wd4305",  # 'identifier' : truncation from 'type1' to 'type2'
+        "/wd4389",  # 'operator' : signed/unsigned mismatch
+
+        "/wd4702",  # unreachable code
+
+        # http://crbug.com/848979 - MSVC is more conservative than Clang with
+        # regards to variables initialized and consumed in different branches.
+        "/wd4701",  # Potentially uninitialized local variable 'name' used
+        "/wd4703",  # Potentially uninitialized local pointer variable 'name'
+                    # used
+
+        # http://crbug.com/848979 - Remaining Clang permitted warnings.
+        "/wd4661",  # 'identifier' : no suitable definition provided for
+                    # explicit
+                    # template instantiation request
+
+        "/wd4706",  # assignment within conditional expression
+                    # MSVC is stricter and requires a boolean expression.
+
+        "/wd4715",  # 'function' : not all control paths return a value'
+                    # MSVC does not analyze switch (enum) for completeness.
+      ]
+
+      cflags_cc += [
+        # Allow "noexcept" annotations even though we compile with exceptions
+        # disabled.
+        "/wd4577",
+      ]
+
+      if (current_cpu == "x86") {
+        cflags += [
+          # VC++ 2015 changes 32-bit size_t truncation warnings from 4244 to
+          # 4267. Example: short TruncTest(size_t x) { return x; }
+          # Since we disable 4244 we need to disable 4267 during migration.
+          # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+          "/wd4267",
+        ]
+
+        if (msvc_use_sse2) {
+          cflags += [ "/arch:SSE2" ]
+        }
+      }
+    }
+  }
+
+  if (is_clang) {
+    # Override -Wno-c++11-narrowing.
+    cflags += [ "-Wc++11-narrowing" ]
+
+    # TODO(crbug.com/1213098): Remove once this is in //build.
+    cflags += [ "-Wdeprecated-copy" ]
+
+    # May flag some issues when converting int to size_t.
+    cflags += [ "-Wtautological-unsigned-zero-compare" ]
+
+    # Catch misuse of cppgc in XFA.
+    if (pdf_enable_xfa && clang_use_chrome_plugins) {
+      cflags += [
+        "-Xclang",
+        "-add-plugin",
+        "-Xclang",
+        "blink-gc-plugin",
+      ]
+    }
+  }
+
+  if (!is_win && !is_clang) {
+    cflags += [
+      # Override -Wno-narrowing for GCC.
+      "-Wnarrowing",
+
+      # GCC assumes that control can get past an exhaustive switch and then
+      # warns if there's no return there.
+      "-Wno-return-type",
+    ]
   }
 }
 
@@ -72,10 +272,6 @@
       }
     }
   }
-
-  if (pdf_use_win32_gdi) {
-    defines += [ "PDFIUM_PRINT_TEXT_WITH_GDI" ]
-  }
 }
 
 config("pdfium_core_config") {
@@ -86,21 +282,27 @@
     "//build/config/compiler:noshadowing",
   ]
   defines = []
-  if (is_linux) {
-    if (current_cpu == "x64") {
-      defines += [ "_FX_CPU_=_FX_X64_" ]
-      cflags += [ "-fPIC" ]
-    } else if (current_cpu == "x86") {
-      defines += [ "_FX_CPU_=_FX_X86_" ]
-    }
-  }
   if (is_win) {
     cflags += [
       "/wd4324",
       "/wd4577",
     ]
   }
-  configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
+}
+
+config("pdfium_strict_config") {
+  configs = [
+    ":pdfium_core_config",
+    "//build/config/compiler:wexit_time_destructors",
+    "//build/config/compiler:wglobal_constructors",
+  ]
+}
+
+config("pdfium_noshorten_config") {
+  cflags = []
+  if (is_clang) {
+    cflags += [ "-Wshorten-64-to-32" ]
+  }
 }
 
 source_set("pdfium_public_headers_impl") {
@@ -122,6 +324,7 @@
     "public/fpdf_progressive.h",
     "public/fpdf_save.h",
     "public/fpdf_searchex.h",
+    "public/fpdf_signature.h",
     "public/fpdf_structtree.h",
     "public/fpdf_sysfontinfo.h",
     "public/fpdf_text.h",
@@ -139,8 +342,9 @@
 }
 
 component("pdfium") {
+  output_name = "pdfium"
   libs = []
-  configs += [ ":pdfium_core_config" ]
+  configs += [ ":pdfium_strict_config" ]
   public_configs = [ ":pdfium_public_config" ]
 
   deps = [
@@ -180,7 +384,7 @@
   }
 
   if (is_mac) {
-    libs += [
+    frameworks = [
       "AppKit.framework",
       "CoreFoundation.framework",
     ]
@@ -191,24 +395,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,7 +434,6 @@
     "core/fxcrt:unittests",
     "core/fxge:unittests",
     "fpdfsdk:unittests",
-    "testing:test_support",
     "testing:unit_test_support",
     "//testing/gmock",
     "//testing/gtest",
@@ -255,18 +450,20 @@
       "fxjs:unittests",
       "//v8",
     ]
-  }
 
-  if (pdf_enable_xfa) {
-    deps += [
-      "fxbarcode:unittests",
-      "xfa/fde:unittests",
-      "xfa/fgas:unittests",
-      "xfa/fgas/layout:unittests",
-      "xfa/fxfa:unittests",
-      "xfa/fxfa/fm2js:unittests",
-      "xfa/fxfa/parser:unittests",
-    ]
+    if (pdf_enable_xfa) {
+      deps += [
+        "core/fxcrt/css:unittests",
+        "fxbarcode:unittests",
+        "xfa/fde:unittests",
+        "xfa/fgas/crt:unittests",
+        "xfa/fgas/font:unittests",
+        "xfa/fgas/layout:unittests",
+        "xfa/fxfa:unittests",
+        "xfa/fxfa/formcalc:unittests",
+        "xfa/fxfa/parser:unittests",
+      ]
+    }
   }
 }
 
@@ -276,7 +473,6 @@
     ":pdfium_public_headers",
     "core/fxcrt",
     "testing:embedder_test_support",
-    "testing:test_support",
     "third_party:pdfium_base_test_support",
     "//testing/gmock",
     "//testing/gtest",
@@ -293,6 +489,7 @@
   testonly = true
   sources = [ "testing/embedder_test_main.cpp" ]
   deps = [
+    ":pdfium_embeddertest_deps",
     "core/fpdfapi/edit:embeddertests",
     "core/fpdfapi/parser:embeddertests",
     "core/fpdfapi/render:embeddertests",
@@ -300,8 +497,8 @@
     "core/fxcrt",
     "core/fxge:embeddertests",
     "fpdfsdk:embeddertests",
+    "fpdfsdk/formfiller:embeddertests",
     "fpdfsdk/pwl:embeddertests",
-    "testing:test_support",
     "testing/image_diff",
     "//testing/gmock",
     "//testing/gtest",
@@ -336,14 +533,12 @@
   testonly = true
   sources = [ "testing/image_diff/image_diff.cpp" ]
   deps = [
-    ":pdfium",
     "core/fxcrt",
+    "testing:path_service",
     "testing/image_diff",
     "//build/win:default_exe_manifest",
   ]
-  configs -= [ "//build/config/compiler:chromium_code" ]
-  configs += [ "//build/config/compiler:no_chromium_code" ]
-  configs += [ ":pdfium_core_config" ]
+  configs += [ ":pdfium_strict_config" ]
 }
 
 if (pdf_is_standalone) {
@@ -372,3 +567,16 @@
     ]
   }
 }
+
+# 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..e5aa4d8 100644
--- a/DEPS
+++ b/DEPS
@@ -1,132 +1,429 @@
 use_relative_paths = True
 
+gclient_gn_args_file = 'build/config/gclient_args.gni'
+gclient_gn_args = [
+  'checkout_skia',
+]
+
 vars = {
   # By default, we should check out everything needed to run on the main
-  # chromium waterfalls. This var can be also be set to "small", in order
-  # to skip things are not strictly needed to build chromium for development
-  # purposes.
+  # pdfium waterfalls. This var can be also be set to 'small', in order to skip
+  # things are not strictly needed to build pdfium for development purposes,
+  # by adding the following line to the .gclient file inside a solutions entry:
+  #      "custom_vars": { "checkout_configuration": "small" },
+  # Similarly, this var can be set to 'minimal' to also skip the Skia and V8
+  # checkouts for the smallest possible checkout, where some features will not
+  # work.
   'checkout_configuration': 'default',
 
-  'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration != "small"',
+  'checkout_instrumented_libraries': 'checkout_linux and checkout_configuration != "small" and checkout_configuration != "minimal"',
+
+  'checkout_skia': 'checkout_configuration != "minimal"',
+
+  'checkout_testing_corpus': 'checkout_configuration != "small" and checkout_configuration != "minimal"',
+
+  'checkout_v8': 'checkout_configuration != "minimal"',
+
+  # By default, download the fuchsia sdk from the public sdk directory.
+  'fuchsia_sdk_cipd_prefix': 'fuchsia/sdk/gn/',
 
   'chromium_git': 'https://chromium.googlesource.com',
   'pdfium_git': 'https://pdfium.googlesource.com',
+  'skia_git': 'https://skia.googlesource.com',
 
-  'android_ndk_revision': '27c0a8d090c666a50e40fceb4ee5b40b1a2d3f87',
-  'binutils_revision': '01aa7745b0bab64ae22600f09fd6483c60f22ebf',
-  'build_revision': '1bee638a8c4a9481ea06df4982d69488d0a5626d',
-  'buildtools_revision': '1f38b432e5630619f3aba0a22b9b63d606aee35a',
-  'catapult_revision': 'f7d73bb520283d2a06b8fde8a1b02aa33414fcd0',
-  'clang_revision': '42fbdfef1ce265b09dc6bda2ed90d83324c97481',
-  'code_coverage_revision': 'c7a868bacaccf4f52848e04564fb7de0671e0727',
-  'depot_tools_revision': 'e9730d75a00548a22e4392567243969d85c02dd4',
-  'freetype_revision': 'e5038be70414cf66da6c4d5ce4e30375884c30d8',
-  'gtest_revision': '5395345ca4f0c596110188688ed990e0de5a181c',
-  'icu_revision': 'dbd3825b31041d782c5b504c59dcfb5ac7dda08c',
-  'instrumented_lib_revision': '4dca59c6a614b08b394ed6154a8fcded9298b07e',
-  'jinja2_revision': 'b41863e42637544c2941b574c7877d3e1f663e25',
-  'jpeg_turbo_revision': 'ce0e57e8e636f5132fe6f0590a4dba91f92fd935',
-  'markupsafe_revision': '8f45f5cfa0009d2a70589bcda0349b8cb2b72783',
-  'pdfium_tests_revision': '02dd653ec62649b6f1aa4e4526071cc32d903f54',
-  'skia_revision': 'd50cc95872a8a832faea0154f7ea1fd56cebc775',
-  'tools_memory_revision': 'f7b00daf4df7f6c469f5fbc68d7f40f6bd15d6e6',
-  'trace_event_revision': '81c050f857a0e3c960cfd87f37e3d30d2ef78718',
-  'v8_revision': 'cd34145326def51cb6dcf87aed7d0caf9f62bb4f',
-  'yasm_source_revision': '720b70524a4424b15fc57e82263568c8ba0496ad',
-  'zlib_revision': '814da1f383b625955149c3845db62af3f29a4ffe',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling abseil
+  # and whatever else without interference from each other.
+  'abseil_revision': '20c8ae002db022653b94e80dec69306558818ebf',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling android_ndk
+  # and whatever else without interference from each other.
+  'android_ndk_revision': '8388a2be5421311dc75c5f937aae13d821a27f3d',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling build
+  # and whatever else without interference from each other.
+  'build_revision': 'c44ccbfc028a136e7d39bf79e45a92a91d7b5beb',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling buildtools
+  # and whatever else without interference from each other.
+  'buildtools_revision': '3ee69a5c6bc8d115dea09bbf8e536f529e7e12a8',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling catapult
+  # and whatever else without interference from each other.
+  'catapult_revision': 'ac30cc4bc7d30e574625e1f6a77fba5df0719ed6',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling clang format
+  # and whatever else without interference from each other.
+  'clang_format_revision': 'f97059df7f8b205064625cdb5f97b56668a125ef',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling clang
+  # and whatever else without interference from each other.
+  'clang_revision': 'f6862472607fa628642713b615a9e119d51bd43d',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling code_coverage
+  # and whatever else without interference from each other.
+  'code_coverage_revision': '9b51524c4f7575ee90d9173507e6f13f814d7001',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling depot_tools
+  # and whatever else without interference from each other.
+  'depot_tools_revision': 'ab117384e6c0384ba5aab66c13d8f1008d964655',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling freetype
+  # and whatever else without interference from each other.
+  'freetype_revision': 'b0a4f99278aa7e14bd1d0d9e40ad28dce543fde6',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling Fuchsia sdk
+  # and whatever else without interference from each other.
+  'fuchsia_version': 'version:12.20230330.3.1',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling GN CIPD package version
+  # and whatever else without interference from each other.
+  'gn_version': 'git_revision:41fef642de70ecdcaaa26be96d56a0398f95abd4',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling gtest
+  # and whatever else without interference from each other.
+  'gtest_revision': 'af29db7ec28d6df1c7f0f745186884091e602e07',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling icu
+  # and whatever else without interference from each other.
+  'icu_revision': '1e49ac26ddc712b1ab702f69023cbc57e9ae6628',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling instrumented_lib
+  # and whatever else without interference from each other.
+  'instrumented_lib_revision': '0f536d22dbed454b1254c7e6d7130eab28fba1fa',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling jinja2
+  # and whatever else without interference from each other.
+  'jinja2_revision': '264c07d7e64f2874434a3b8039e101ddf1b01e7e',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling jpeg_turbo
+  # and whatever else without interference from each other.
+  'jpeg_turbo_revision': 'aa4075f116e4312537d0d3e9dbd5e31096539f94',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling libc++
+  # and whatever else without interference from each other.
+  # If you change this, also update the libc++ revision in
+  # //buildtools/deps_revisions.gni.
+  'libcxx_revision': 'ab37483b426c16ce33f8f0064be571513d5a8c34',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling libc++abi
+  # and whatever else without interference from each other.
+  'libcxxabi_revision': '4a9d0560b481a96821bec591325b50a5063f4a32',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling libpng
+  # and whatever else without interference from each other.
+  'libpng_revision': '805df541c44099bb20d425ac47c666e29b1f7a80',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling libunwind
+  # and whatever else without interference from each other.
+  'libunwind_revision': 'f3464caa6aa83a5eb924a5ae3779e974bd1bebf4',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling lss
+  # and whatever else without interference from each other.
+  'lss_revision': 'ce877209e11aa69dcfffbd53ef90ea1d07136521',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling markupsafe
+  # and whatever else without interference from each other.
+  'markupsafe_revision': '13f4e8c9e206567eeb13bf585406ddc574005748',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling nasm_source
+  # and whatever else without interference from each other.
+  'nasm_source_revision': '7fc833e889d1afda72c06220e5bed8fb43b2e5ce',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling Ninja CIPD package version
+  # and whatever else without interference from each other.
+  'ninja_version': 'version:2@1.11.1.chromium.6',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling partition_allocator
+  # and whatever else without interference from each other.
+  'partition_allocator_revision': '118936d6793d0c259bd9023717d37367a7b04320',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling pdfium_tests
+  # and whatever else without interference from each other.
+  'pdfium_tests_revision': 'd1386521f5d606b9110143aa40b23651b17aded2',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling resultdb
+  # and whatever else without interference from each other.
+  'resultdb_version': 'git_revision:ebc74d10fa0d64057daa6f128e89f3672eeeec95',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling skia
+  # and whatever else without interference from each other.
+  'skia_revision': 'ad459a5b8df474d881209696b3fb4f038e0d3a55',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling test_fonts
+  # and whatever else without interference from each other.
+  'test_fonts_revision': '7f51783942943e965cd56facf786544ccfc07713',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling tools_memory
+  # and whatever else without interference from each other.
+  'tools_memory_revision': '13f0b81ce581364c5f0f2e9e16d6120073dc56a6',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling trace_event
+  # and whatever else without interference from each other.
+  'trace_event_revision': '147f65333c38ddd1ebf554e89965c243c8ce50b3',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling v8
+  # and whatever else without interference from each other.
+  'v8_revision': 'f66b0bc93b1edc09a6ff66c2e0ae0f2848d90be7',
+  # Three lines of non-changing comments so that
+  # the commit queue can handle CLs rolling zlib
+  # and whatever else without interference from each other.
+  'zlib_revision': 'b890619bc2b193b8fbe9c1c053f4cd19a9791d92',
 }
 
+# Only these hosts are allowed for dependencies in this DEPS file.
+# If you need to add a new host, and the new host is not in Chromium's DEPS
+# file's allowed_hosts list, contact Chrome infrastructure team.
+allowed_hosts = [
+  'chromium.googlesource.com',
+  'pdfium.googlesource.com',
+  'skia.googlesource.com',
+]
+
 deps = {
-  "base/trace_event/common":
-    Var('chromium_git') + "/chromium/src/base/trace_event/common.git@" +
+  'base/allocator/partition_allocator':
+    Var('chromium_git') +
+        '/chromium/src/base/allocator/partition_allocator.git@' +
+        Var('partition_allocator_revision'),
+
+  'base/trace_event/common':
+    Var('chromium_git') + '/chromium/src/base/trace_event/common.git@' +
         Var('trace_event_revision'),
 
-  "build":
-    Var('chromium_git') + "/chromium/src/build.git@" + Var('build_revision'),
+  'build':
+    Var('chromium_git') + '/chromium/src/build.git@' + Var('build_revision'),
 
-  "buildtools":
-    Var('chromium_git') + "/chromium/src/buildtools.git@" +
+  'buildtools':
+    Var('chromium_git') + '/chromium/src/buildtools.git@' +
         Var('buildtools_revision'),
 
-  "testing/corpus":
-    Var('pdfium_git') + "/pdfium_tests@" + Var('pdfium_tests_revision'),
+  'buildtools/clang_format/script':
+    Var('chromium_git') +
+        '/external/github.com/llvm/llvm-project/clang/tools/clang-format.git@' +
+        Var('clang_format_revision'),
 
-  "third_party/android_ndk": {
-    'url': Var('chromium_git') + "/android_ndk.git@" + Var('android_ndk_revision'),
+  'buildtools/linux64': {
+    'packages': [
+      {
+        'package': 'gn/gn/linux-amd64',
+        'version': Var('gn_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'host_os == "linux"',
+  },
+
+  'buildtools/mac': {
+    'packages': [
+      {
+        'package': 'gn/gn/mac-${{arch}}',
+        'version': Var('gn_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'host_os == "mac"',
+  },
+
+  'buildtools/third_party/libc++/trunk':
+    Var('chromium_git') +
+        '/external/github.com/llvm/llvm-project/libcxx.git@' +
+        Var('libcxx_revision'),
+
+  'buildtools/third_party/libc++abi/trunk':
+    Var('chromium_git') +
+        '/external/github.com/llvm/llvm-project/libcxxabi.git@' +
+        Var('libcxxabi_revision'),
+
+  'buildtools/third_party/libunwind/trunk':
+    Var('chromium_git') +
+        '/external/github.com/llvm/llvm-project/libunwind.git@' +
+        Var('libunwind_revision'),
+
+  'buildtools/win': {
+    'packages': [
+      {
+        'package': 'gn/gn/windows-amd64',
+        'version': Var('gn_version'),
+      }
+    ],
+    'dep_type': 'cipd',
+    'condition': 'host_os == "win"',
+  },
+
+  '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_ndk': {
+    'url': Var('chromium_git') + '/android_ndk.git@' +
+        Var('android_ndk_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/catapult': {
+    'url': Var('chromium_git') + '/catapult.git@' + Var('catapult_revision'),
     'condition': 'checkout_android',
   },
 
   'third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' +
+    Var('chromium_git') + '/chromium/tools/depot_tools.git@' +
         Var('depot_tools_revision'),
 
-  "third_party/freetype/src":
+  'third_party/freetype/src':
     Var('chromium_git') + '/chromium/src/third_party/freetype2.git@' +
         Var('freetype_revision'),
 
-  "third_party/googletest/src":
-    Var('chromium_git') + '/external/github.com/google/googletest.git' + '@' +
+  'third_party/fuchsia-sdk/sdk': {
+      'packages': [
+          {
+              'package': Var('fuchsia_sdk_cipd_prefix') + '${{platform}}',
+              'version': Var('fuchsia_version'),
+          },
+      ],
+      'condition': 'checkout_fuchsia',
+      'dep_type': 'cipd',
+  },
+
+  'third_party/googletest/src':
+    Var('chromium_git') + '/external/github.com/google/googletest.git@' +
         Var('gtest_revision'),
 
-  "third_party/icu":
-    Var('chromium_git') + "/chromium/deps/icu.git@" + Var('icu_revision'),
+  'third_party/icu':
+    Var('chromium_git') + '/chromium/deps/icu.git@' + Var('icu_revision'),
 
-  "third_party/instrumented_libraries":
+  'third_party/instrumented_libraries':
     Var('chromium_git') +
-        "/chromium/src/third_party/instrumented_libraries.git@" +
+        '/chromium/src/third_party/instrumented_libraries.git@' +
         Var('instrumented_lib_revision'),
 
-  "third_party/jinja2":
-    Var('chromium_git') + "/chromium/src/third_party/jinja2.git@" +
+  'third_party/jinja2':
+    Var('chromium_git') + '/chromium/src/third_party/jinja2.git@' +
         Var('jinja2_revision'),
 
-  "third_party/markupsafe":
-    Var('chromium_git') + "/chromium/src/third_party/markupsafe.git@" +
-        Var('markupsafe_revision'),
-
-  "third_party/libjpeg_turbo":
-    Var('chromium_git') + "/chromium/deps/libjpeg_turbo.git@" +
+  '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/lss': {
+      'url': Var('chromium_git') + '/linux-syscall-support.git' + '@' + Var('lss_revision'),
+      'condition': 'checkout_android or checkout_linux',
+  },
+
+  'third_party/markupsafe':
+    Var('chromium_git') + '/chromium/src/third_party/markupsafe.git@' +
+        Var('markupsafe_revision'),
+
+  'third_party/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 +432,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 +478,177 @@
     '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'],
   },
 ]
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 4eaf791..0000000
--- a/METADATA
+++ /dev/null
@@ -1,9 +0,0 @@
-# *** THIS PACKAGE HAS SPECIAL LICENSING CONDITIONS.  PLEASE
-#     CONSULT THE OWNERS AND opensource-licensing@google.com BEFORE
-#     DEPENDING ON IT IN YOUR PROJECT. ***
-third_party {
-  license_note: "would be NOTICE save for OFL 1.1 in:\n"
-  "   testing/resources/pixel/bug_925736.pdf\n"
-  "   testing/resources/text_font.pdf"
-  license_type: BY_EXCEPTION_ONLY
-}
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 9ad4abe..236f896 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|skia))?(_(linux|mac|win))?.pdf.\d+.png
+
+  This must be the same format as the one in testing/corpus's PRESUBMIT.py.
+  """
   expected_pattern = input_api.re.compile(
-      r'.+_expected(_(win|mac|linux))?\.pdf\.\d+.png')
+      r'.+_expected(_(agg|skia))?(_(linux|mac|win))?\.pdf\.\d+.png')
   results = []
   for f in input_api.AffectedFiles(include_deletes=False):
     if not f.LocalPath().endswith('.png'):
       continue
     if expected_pattern.match(f.LocalPath()):
       continue
-    results.append(output_api.PresubmitError(
-        'PNG file %s does not have the correct format' % f.LocalPath()))
+    results.append(
+        output_api.PresubmitError(
+            'PNG file %s does not have the correct format' % f.LocalPath()))
   return results
 
-def CheckChangeOnUpload(input_api, output_api):
-  cpp_source_filter = lambda x: input_api.FilterSourceFile(
-      x, white_list=(r'\.(?:c|cc|cpp|h)$',))
 
+def _CheckUselessForwardDeclarations(input_api, output_api):
+  """Checks that added or removed lines in non third party affected
+     header files do not lead to new useless class or struct forward
+     declaration.
+  """
   results = []
-  results += _CheckUnwantedDependencies(input_api, output_api)
-  results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
-  results += input_api.canned_checks.CheckChangeLintsClean(
-      input_api, output_api, cpp_source_filter, LINT_FILTERS)
-  results += _CheckIncludeOrder(input_api, output_api)
-  results += _CheckTestDuplicates(input_api, output_api)
-  results += _CheckPNGFormat(input_api, output_api)
+  class_pattern = input_api.re.compile(r'^class\s+(\w+);$',
+                                       input_api.re.MULTILINE)
+  struct_pattern = input_api.re.compile(r'^struct\s+(\w+);$',
+                                        input_api.re.MULTILINE)
+  for f in input_api.AffectedFiles(include_deletes=False):
+    if f.LocalPath().startswith('third_party'):
+      continue
+
+    if not f.LocalPath().endswith('.h'):
+      continue
+
+    contents = input_api.ReadFile(f)
+    fwd_decls = input_api.re.findall(class_pattern, contents)
+    fwd_decls.extend(input_api.re.findall(struct_pattern, contents))
+
+    useless_fwd_decls = []
+    for decl in fwd_decls:
+      count = sum(
+          1
+          for _ in input_api.re.finditer(r'\b%s\b' %
+                                         input_api.re.escape(decl), contents))
+      if count == 1:
+        useless_fwd_decls.append(decl)
+
+    if not useless_fwd_decls:
+      continue
+
+    for line in f.GenerateScmDiff().splitlines():
+      if (line.startswith('-') and not line.startswith('--') or
+          line.startswith('+') and not line.startswith('++')):
+        for decl in useless_fwd_decls:
+          if input_api.re.search(r'\b%s\b' % decl, line[1:]):
+            results.append(
+                output_api.PresubmitPromptWarning(
+                    '%s: %s forward declaration is no longer needed' %
+                    (f.LocalPath(), decl)))
+            useless_fwd_decls.remove(decl)
+
+  return results
+
+
+def ChecksCommon(input_api, output_api):
+  results = []
+
+  results.extend(
+      input_api.canned_checks.PanProjectChecks(
+          input_api, output_api, project_name='PDFium'))
+
+  # PanProjectChecks() doesn't consider .gn/.gni files, so check those, too.
+  files_to_check = (
+      r'.*\.gn$',
+      r'.*\.gni$',
+  )
+  results.extend(
+      input_api.canned_checks.CheckLicense(
+          input_api,
+          output_api,
+          project_name='PDFium',
+          source_file_filter=lambda x: input_api.FilterSourceFile(
+              x, files_to_check=files_to_check)))
+
+  return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  results = []
+  results.extend(_CheckNoBannedFunctions(input_api, output_api))
+  results.extend(_CheckUnwantedDependencies(input_api, output_api))
+  results.extend(
+      input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
+  results.extend(
+      input_api.canned_checks.CheckChangeLintsClean(
+          input_api, output_api, lint_filters=LINT_FILTERS))
+  results.extend(_CheckIncludeOrder(input_api, output_api))
+  results.extend(_CheckLibcxxRevision(input_api, output_api))
+  results.extend(_CheckTestDuplicates(input_api, output_api))
+  results.extend(_CheckPngNames(input_api, output_api))
+  results.extend(_CheckUselessForwardDeclarations(input_api, output_api))
+
+  author = input_api.change.author_email
+  if author and author not in _KNOWN_ROBOTS:
+    results.extend(
+        input_api.canned_checks.CheckAuthorizedAuthor(input_api, output_api))
+
+  for f in input_api.AffectedFiles():
+    path, name = input_api.os_path.split(f.LocalPath())
+    if name == 'PRESUBMIT.py':
+      full_path = input_api.os_path.join(input_api.PresubmitLocalPath(), path)
+      test_file = input_api.os_path.join(path, 'PRESUBMIT_test.py')
+      if f.Action() != 'D' and input_api.os_path.exists(test_file):
+        # The PRESUBMIT.py file (and the directory containing it) might
+        # have been affected by being moved or removed, so only try to
+        # run the tests if they still exist.
+        results.extend(
+            input_api.canned_checks.RunUnitTestsInDirectory(
+                input_api,
+                output_api,
+                full_path,
+                files_to_check=[r'^PRESUBMIT_test\.py$'],
+                run_on_python2=not USE_PYTHON3,
+                run_on_python3=USE_PYTHON3,
+                skip_shebang_check=True))
 
   return results
diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py
new file mode 100755
index 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..657cfda 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.
 
@@ -96,30 +83,17 @@
 
 # Set true to enable experimental Skia backend.
 pdf_use_skia = false
-# Set true to enable experimental Skia backend (paths only).
-pdf_use_skia_paths = false
 
 pdf_enable_xfa = true  # Set false to remove XFA support (implies JS support).
 pdf_enable_v8 = true  # Set false to remove Javascript support.
 pdf_is_standalone = true  # Set for a non-embedded build.
 is_component_build = false # Disable component build (Though it should work)
-
-clang_use_chrome_plugins = false  # Currently must be false.
 ```
 
 For sample applications like `pdfium_test` to build, one must set
 `pdf_is_standalone = true`.
 
-By default, the entire project builds with C++14, because features like V8
-support, XFA support, and the Skia backend all have dependencies on libraries
-that require C++14. If one does not need any of those features, and need to fall
-back to building in C++11 mode, then set `use_cxx11 = true`. This fallback is
-temporary and will go away in the future when PDFium fully transitions to C++14.
-See [this bug](https://crbug.com/pdfium/1407) for details.
-
-When building with the experimental Skia backend, Skia itself it built with
-C++17. There is no configuration for this. One just has to use a build toolchain
-that supports C++17.
+By default, the entire project builds with C++17.
 
 When complete the arguments will be stored in `<directory>/args.gn`, and
 GN will automatically use the new arguments to generate build files.
@@ -190,7 +164,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 +182,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 +198,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 +215,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/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..2b9bbd6
--- /dev/null
+++ b/build_overrides/BUILDCONFIG.gn
@@ -0,0 +1,627 @@
+# Copyright 2022 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# =============================================================================
+# WHAT IS THIS FILE?
+# =============================================================================
+#
+# This is a copy of //build/config/BUILDCONFIG.gn. The difference is it adds an
+# extra default_compiler_configs to use PDFium's desired default compiler
+# config. See "PDFIUM MODIFICATIONS" below.
+#
+# This is the main GN build configuration. This file is loaded after the
+# build args (args.gn) for the build directory and after the toplevel ".gn"
+# file (which points to this file as the build configuration).
+#
+# This file will be executed and the resulting context will be used to execute
+# every other file in the build. So variables declared here (that don't start
+# with an underscore) will be implicitly global.
+
+# =============================================================================
+# PLATFORM SELECTION
+# =============================================================================
+#
+# There are two main things to set: "os" and "cpu". The "toolchain" is the name
+# of the GN thing that encodes combinations of these things.
+#
+# Users typically only set the variables "target_os" and "target_cpu" in "gn
+# args", the rest are set up by our build and internal to GN.
+#
+# There are three different types of each of these things: The "host"
+# represents the computer doing the compile and never changes. The "target"
+# represents the main thing we're trying to build. The "current" represents
+# which configuration is currently being defined, which can be either the
+# host, the target, or something completely different (like nacl). GN will
+# run the same build file multiple times for the different required
+# configuration in the same build.
+#
+# This gives the following variables:
+#  - host_os, host_cpu, host_toolchain
+#  - target_os, target_cpu, default_toolchain
+#  - current_os, current_cpu, current_toolchain.
+#
+# Note the default_toolchain isn't symmetrical (you would expect
+# target_toolchain). This is because the "default" toolchain is a GN built-in
+# concept, and "target" is something our build sets up that's symmetrical with
+# its GYP counterpart. Potentially the built-in default_toolchain variable
+# could be renamed in the future.
+#
+# When writing build files, to do something only for the host:
+#   if (current_toolchain == host_toolchain) { ...
+
+if (target_os == "") {
+  target_os = host_os
+}
+
+if (target_cpu == "") {
+  if (target_os == "android") {
+    # If we're building for Android, we should assume that we want to
+    # build for ARM by default, not the host_cpu (which is likely x64).
+    # This allows us to not have to specify both target_os and target_cpu
+    # on the command line.
+    target_cpu = "arm"
+  } else {
+    target_cpu = host_cpu
+  }
+}
+
+if (current_cpu == "") {
+  current_cpu = target_cpu
+}
+if (current_os == "") {
+  current_os = target_os
+}
+
+# =============================================================================
+# BUILD FLAGS
+# =============================================================================
+#
+# This block lists input arguments to the build, along with their default
+# values.
+#
+# If a value is specified on the command line, it will overwrite the defaults
+# given in a declare_args block, otherwise the default will be used.
+#
+# YOU SHOULD ALMOST NEVER NEED TO ADD FLAGS TO THIS FILE. GN allows any file in
+# the build to declare build flags. If you need a flag for a single component,
+# you can just declare it in the corresponding BUILD.gn file.
+#
+# - If your feature is a single target, say //components/foo, you can put
+#   a declare_args() block in //components/foo/BUILD.gn and use it there.
+#   Nobody else in the build needs to see the flag.
+#
+# - Defines based on build variables should be implemented via the generated
+#   build flag header system. See //build/buildflag_header.gni. You can put
+#   the buildflag_header target in the same file as the build flag itself. You
+#   should almost never set "defines" directly.
+#
+# - If your flag toggles a target on and off or toggles between different
+#   versions of similar things, write a "group" target that forwards to the
+#   right target (or no target) depending on the value of the build flag. This
+#   group can be in the same BUILD.gn file as the build flag, and targets can
+#   depend unconditionally on the group rather than duplicating flag checks
+#   across many targets.
+#
+# - If a semi-random set of build files REALLY needs to know about a define and
+#   the above pattern for isolating the build logic in a forwarding group
+#   doesn't work, you can put the argument in a .gni file. This should be put
+#   in the lowest level of the build that knows about this feature (which should
+#   almost always be outside of the //build directory!).
+#
+# Other flag advice:
+#
+# - Use boolean values when possible. If you need a default value that expands
+#   to some complex thing in the default case (like the location of the
+#   compiler which would be computed by a script), use a default value of -1 or
+#   the empty string. Outside of the declare_args block, conditionally expand
+#   the default value as necessary.
+#
+# - Use a name like "use_foo" or "is_foo" (whatever is more appropriate for
+#   your feature) rather than just "foo".
+#
+# - Write good comments directly above the declaration with no blank line.
+#   These comments will appear as documentation in "gn args --list".
+#
+# - Don't call exec_script inside declare_args. This will execute the script
+#   even if the value is overridden, which is wasteful. See first bullet.
+
+declare_args() {
+  # Set to enable the official build level of optimization. This has nothing
+  # to do with branding, but enables an additional level of optimization above
+  # release (!is_debug). This might be better expressed as a tri-state
+  # (debug, release, official) but for historical reasons there are two
+  # separate flags.
+  #
+  # IMPORTANT NOTE: (!is_debug) is *not* sufficient to get satisfying
+  # performance. In particular, DCHECK()s are still enabled for release builds,
+  # which can halve overall performance, and do increase memory usage. Always
+  # set "is_official_build" to true for any build intended to ship to end-users.
+  is_official_build = false
+
+  # Set to true when compiling with the Clang compiler.
+  is_clang = current_os != "linux" ||
+             (current_cpu != "s390x" && current_cpu != "s390" &&
+              current_cpu != "ppc64" && current_cpu != "ppc" &&
+              current_cpu != "mips" && current_cpu != "mips64" &&
+              current_cpu != "riscv64")
+
+  # Allows the path to a custom target toolchain to be injected as a single
+  # argument, and set as the default toolchain.
+  custom_toolchain = ""
+
+  # This should not normally be set as a build argument.  It's here so that
+  # every toolchain can pass through the "global" value via toolchain_args().
+  host_toolchain = ""
+
+  # Do not set this directly.
+  # It should be set only by //build/toolchains/android:robolectric_x64.
+  # True when compiling native code for use with robolectric_binary().
+  is_robolectric = false
+
+  # DON'T ADD MORE FLAGS HERE. Read the comment above.
+}
+
+declare_args() {
+  # Debug build. Enabling official builds automatically sets is_debug to false.
+  is_debug = !is_official_build
+}
+
+declare_args() {
+  # Component build. Setting to true compiles targets declared as "components"
+  # as shared libraries loaded dynamically. This speeds up development time.
+  # When false, components will be linked statically.
+  #
+  # For more information see
+  # https://chromium.googlesource.com/chromium/src/+/main/docs/component_build.md
+  is_component_build = is_debug && current_os != "ios"
+}
+
+assert(!(is_debug && is_official_build), "Can't do official debug builds")
+assert(!(current_os == "ios" && is_component_build),
+       "Can't use component build on iOS")
+
+# ==============================================================================
+# TOOLCHAIN SETUP
+# ==============================================================================
+#
+# Here we set the default toolchain, as well as the variable host_toolchain
+# which will identify the toolchain corresponding to the local system when
+# doing cross-compiles. When not cross-compiling, this will be the same as the
+# default toolchain.
+#
+# We do this before anything else to make sure we complain about any
+# unsupported os/cpu combinations as early as possible.
+
+if (host_toolchain == "") {
+  # This should only happen in the top-level context.
+  # In a specific toolchain context, the toolchain_args()
+  # block should have propagated a value down.
+  # TODO(dpranke): Add some sort of assert here that verifies that
+  # no toolchain omitted host_toolchain from its toolchain_args().
+
+  if (host_os == "linux") {
+    if (target_os != "linux") {
+      host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
+    } else if (is_clang) {
+      host_toolchain = "//build/toolchain/linux:clang_$host_cpu"
+    } else {
+      host_toolchain = "//build/toolchain/linux:$host_cpu"
+    }
+  } else if (host_os == "mac") {
+    host_toolchain = "//build/toolchain/mac:clang_$host_cpu"
+  } else if (host_os == "win") {
+    # On Windows always use the target CPU for host builds for x86/x64. On the
+    # configurations we support this will always work and it saves build steps.
+    # Windows ARM64 targets require an x64 host for cross build.
+    if (target_cpu == "x86" || target_cpu == "x64") {
+      if (is_clang) {
+        host_toolchain = "//build/toolchain/win:win_clang_$target_cpu"
+      } else {
+        host_toolchain = "//build/toolchain/win:$target_cpu"
+      }
+    } else if (is_clang) {
+      host_toolchain = "//build/toolchain/win:win_clang_$host_cpu"
+    } else {
+      host_toolchain = "//build/toolchain/win:$host_cpu"
+    }
+  } else if (host_os == "aix") {
+    host_toolchain = "//build/toolchain/aix:$host_cpu"
+  } else if (host_os == "zos") {
+    host_toolchain = "//build/toolchain/zos:$host_cpu"
+  } else {
+    assert(false, "Unsupported host_os: $host_os")
+  }
+}
+
+_default_toolchain = ""
+
+if (target_os == "android") {
+  assert(host_os == "linux", "Android builds are only supported on Linux.")
+  _default_toolchain = "//build/toolchain/android:android_clang_$target_cpu"
+} else if (target_os == "chromeos" || target_os == "linux") {
+  # See comments in build/toolchain/cros/BUILD.gn about board compiles.
+  if (is_clang) {
+    _default_toolchain = "//build/toolchain/linux:clang_$target_cpu"
+  } else {
+    _default_toolchain = "//build/toolchain/linux:$target_cpu"
+  }
+} else if (target_os == "fuchsia") {
+  _default_toolchain = "//build/toolchain/fuchsia:$target_cpu"
+} else if (target_os == "ios") {
+  _default_toolchain = "//build/toolchain/ios:ios_clang_$target_cpu"
+} else if (target_os == "mac") {
+  assert(host_os == "mac" || host_os == "linux",
+         "Mac cross-compiles are unsupported.")
+  _default_toolchain = "//build/toolchain/mac:clang_$target_cpu"
+} else if (target_os == "win") {
+  # On Windows, we use the same toolchain for host and target by default.
+  # Beware, win cross builds have some caveats, see docs/win_cross.md
+  if (is_clang) {
+    _default_toolchain = "//build/toolchain/win:win_clang_$target_cpu"
+  } else {
+    _default_toolchain = "//build/toolchain/win:$target_cpu"
+  }
+} else if (target_os == "winuwp") {
+  # Only target WinUWP on for a Windows store application and only
+  # x86, x64 and arm are supported target CPUs.
+  assert(target_cpu == "x86" || target_cpu == "x64" || target_cpu == "arm" ||
+         target_cpu == "arm64")
+  _default_toolchain = "//build/toolchain/win:uwp_$target_cpu"
+} else if (target_os == "aix") {
+  _default_toolchain = "//build/toolchain/aix:$target_cpu"
+} else if (target_os == "zos") {
+  _default_toolchain = "//build/toolchain/zos:$target_cpu"
+} else {
+  assert(false, "Unsupported target_os: $target_os")
+}
+
+# If a custom toolchain has been set in the args, set it as default. Otherwise,
+# set the default toolchain for the platform (if any).
+if (custom_toolchain != "") {
+  set_default_toolchain(custom_toolchain)
+} else if (_default_toolchain != "") {
+  set_default_toolchain(_default_toolchain)
+}
+
+# =============================================================================
+# OS DEFINITIONS
+# =============================================================================
+#
+# We set these various is_FOO booleans for convenience in writing OS-based
+# conditions.
+#
+# - is_android, is_chromeos, is_ios, and is_win should be obvious.
+# - is_mac is set only for desktop Mac. It is not set on iOS.
+# - is_posix is true for mac and any Unix-like system (basically everything
+#   except Fuchsia and Windows).
+# - is_linux is true for desktop Linux, but not for ChromeOS nor Android (which
+#   is generally too different despite being based on the Linux kernel).
+#
+# Do not add more is_* variants here for random lesser-used Unix systems like
+# aix or one of the BSDs. If you need to check these, just check the
+# current_os value directly.
+
+is_android = current_os == "android"
+is_chromeos = current_os == "chromeos"
+is_fuchsia = current_os == "fuchsia"
+is_ios = current_os == "ios"
+is_linux = current_os == "linux"
+is_mac = current_os == "mac"
+is_nacl = current_os == "nacl"
+is_win = current_os == "win" || current_os == "winuwp"
+
+is_apple = is_ios || is_mac
+is_posix = !is_win && !is_fuchsia
+
+# =============================================================================
+# TARGET DEFAULTS
+# =============================================================================
+#
+# Set up the default configuration for every build target of the given type.
+# The values configured here will be automatically set on the scope of the
+# corresponding target. Target definitions can add or remove to the settings
+# here as needed.
+#
+# WHAT GOES HERE?
+#
+# Other than the main compiler and linker configs, the only reason for a config
+# to be in this list is if some targets need to explicitly override that config
+# by removing it. This is how targets opt-out of flags. If you don't have that
+# requirement and just need to add a config everywhere, reference it as a
+# sub-config of an existing one, most commonly the main "compiler" one.
+
+# Holds all configs used for running the compiler.
+default_compiler_configs = [
+  "//build/config:feature_flags",
+  "//build/config/compiler:afdo",
+  "//build/config/compiler:afdo_optimize_size",
+  "//build/config/compiler:cet_shadow_stack",
+  "//build/config/compiler:chromium_code",
+  "//build/config/compiler:compiler",
+  "//build/config/compiler:compiler_arm_fpu",
+  "//build/config/compiler:compiler_arm_thumb",
+  "//build/config/compiler:default_include_dirs",
+  "//build/config/compiler:default_init_stack_vars",
+  "//build/config/compiler:default_optimization",
+  "//build/config/compiler:default_stack_frames",
+  "//build/config/compiler:default_symbols",
+  "//build/config/compiler:export_dynamic",
+  "//build/config/compiler:no_exceptions",
+  "//build/config/compiler:no_rtti",
+  "//build/config/compiler:no_unresolved_symbols",
+  "//build/config/compiler:runtime_library",
+  "//build/config/compiler:thin_archive",
+  "//build/config/compiler:thinlto_optimize_default",
+  "//build/config/compiler/pgo:default_pgo_flags",
+  "//build/config/coverage:default_coverage",
+  "//build/config/sanitizers:default_sanitizer_flags",
+]
+
+if (is_win) {
+  default_compiler_configs += [
+    "//build/config/win:default_cfg_compiler",
+    "//build/config/win:default_crt",
+    "//build/config/win:lean_and_mean",
+    "//build/config/win:nominmax",
+    "//build/config/win:unicode",
+    "//build/config/win:winver",
+  ]
+}
+
+if (is_posix) {
+  if (current_os != "aix") {
+    default_compiler_configs +=
+        [ "//build/config/gcc:symbol_visibility_hidden" ]
+  }
+}
+
+if (is_fuchsia) {
+  default_compiler_configs += [ "//build/config/gcc:symbol_visibility_hidden" ]
+}
+
+if (is_android) {
+  default_compiler_configs +=
+      [ "//build/config/android:default_orderfile_instrumentation" ]
+}
+
+if (is_clang && !is_nacl) {
+  default_compiler_configs += [
+    "//build/config/clang:find_bad_constructs",
+    "//build/config/clang:extra_warnings",
+  ]
+}
+
+# Debug/release-related defines.
+if (is_debug) {
+  default_compiler_configs += [ "//build/config:debug" ]
+} else {
+  default_compiler_configs += [ "//build/config:release" ]
+}
+
+# =============================================================================
+# Begin PDFIUM MODIFICATIONS
+# =============================================================================
+import("//pdfium.gni")
+if (!pdf_use_cxx20) {
+  if (is_win && !is_clang) {
+    msvc_use_cxx17 = true
+  } else {
+    default_compiler_configs += [ "//build_overrides/compiler:force_cxx17" ]
+  }
+}
+
+# =============================================================================
+# End PDFIUM MODIFICATIONS
+# =============================================================================
+
+# Static libraries and source sets use only the compiler ones.
+set_defaults("static_library") {
+  configs = default_compiler_configs
+}
+set_defaults("source_set") {
+  configs = default_compiler_configs
+}
+set_defaults("rust_library") {
+  configs = default_compiler_configs
+}
+set_defaults("rust_proc_macro") {
+  configs = default_compiler_configs
+}
+
+# Compute the set of configs common to all linked targets (shared libraries,
+# loadable modules, executables) to avoid duplication below.
+if (is_win) {
+  # Many targets remove these configs, so they are not contained within
+  # //build/config:executable_config for easy removal.
+  _linker_configs = [
+    "//build/config/win:default_incremental_linking",
+
+    # Default to console-mode apps. Most of our targets are tests and such
+    # that shouldn't use the windows subsystem.
+    "//build/config/win:console",
+  ]
+} else if (is_mac) {
+  _linker_configs = [ "//build/config/apple:strip_all" ]
+} else {
+  _linker_configs = []
+}
+
+# Executable defaults.
+default_executable_configs = default_compiler_configs + [
+                               "//build/config:default_libs",
+                               "//build/config:executable_config",
+                             ] + _linker_configs
+
+if (is_win) {
+  # Turn on linker CFI for executables, and position it so it can be removed
+  # if needed.
+  default_executable_configs += [ "//build/config/win:cfi_linker" ]
+}
+
+set_defaults("executable") {
+  configs = default_executable_configs
+}
+
+# Shared library and loadable module defaults (also for components in component
+# mode).
+default_shared_library_configs = default_compiler_configs + [
+                                   "//build/config:default_libs",
+                                   "//build/config:shared_library_config",
+                                 ] + _linker_configs
+if (is_win) {
+  # Turn on linker CFI for DLLs, and position it so it can be removed if needed.
+  default_shared_library_configs += [ "//build/config/win:cfi_linker" ]
+}
+
+if (is_android) {
+  # Strip native JNI exports from shared libraries by default. Binaries that
+  # want this can remove this config.
+  default_shared_library_configs +=
+      [ "//build/config/android:hide_all_but_jni_onload" ]
+}
+set_defaults("shared_library") {
+  configs = default_shared_library_configs
+}
+set_defaults("loadable_module") {
+  configs = default_shared_library_configs
+
+  # loadable_modules are generally used by other libs, not just via JNI.
+  if (is_android) {
+    configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+  }
+}
+
+# A helper for forwarding testonly and visibility.
+# Forwarding "*" does not include variables from outer scopes (to avoid copying
+# all globals into each template invocation), so it will not pick up
+# file-scoped or outer-template-scoped variables. Normally this behavior is
+# desired, but "visibility" and "testonly" are commonly defined in outer scopes.
+# Explicitly forwarding them in forward_variables_from() works around this
+# nuance. See //build/docs/writing_gn_templates.md#using-forward_variables_from
+TESTONLY_AND_VISIBILITY = [
+  "testonly",
+  "visibility",
+]
+
+# Sets default dependencies for executable and shared_library targets.
+#
+# Variables
+#   no_default_deps: If true, no standard dependencies will be added.
+#       Targets that set this usually also want to remove
+#       "//build/config/compiler:runtime_library" from configs (to remove
+#       its subconfig "//build/config/c++:runtime_library").
+foreach(_target_type,
+        [
+          "executable",
+          "loadable_module",
+          "shared_library",
+        ]) {
+  template(_target_type) {
+    # Alias "target_name" because it is clobbered by forward_variables_from().
+    _target_name = target_name
+    target(_target_type, _target_name) {
+      forward_variables_from(invoker,
+                             "*",
+                             TESTONLY_AND_VISIBILITY + [ "no_default_deps" ])
+      forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+      if (!defined(deps)) {
+        deps = []
+      }
+      if (!defined(invoker.no_default_deps) || !invoker.no_default_deps) {
+        # This pulls in one of:
+        # //build/config:executable_deps
+        # //build/config:loadable_module_deps
+        # //build/config:shared_library_deps
+        # (This explicit list is so that grepping for these configs finds where
+        # they are used.)
+        deps += [ "//build/config:${_target_type}_deps" ]
+      }
+
+      # On Android, write shared library output file to metadata. We will use
+      # this information to, for instance, collect all shared libraries that
+      # should be packaged into an APK.
+      if (!defined(invoker.metadata) && (is_android || is_robolectric) &&
+          (_target_type == "shared_library" ||
+           _target_type == "loadable_module")) {
+        _output_name = _target_name
+        if (defined(invoker.output_name)) {
+          _output_name = invoker.output_name
+        }
+
+        # Remove 'lib' prefix from output name if it exists.
+        _magic_prefix = "$0x01$0x01"
+        _output_name = string_replace("${_magic_prefix}${_output_name}",
+                                      "${_magic_prefix}lib",
+                                      _magic_prefix,
+                                      1)
+        _output_name = string_replace(_output_name, _magic_prefix, "", 1)
+
+        if (defined(output_extension)) {
+          _shlib_extension = ".$output_extension"
+        } else if (is_component_build && _target_type != "loadable_module") {
+          _shlib_extension = ".cr.so"
+        } else {
+          _shlib_extension = ".so"
+        }
+
+        metadata = {
+          shared_libraries =
+              [ "$root_out_dir/lib${_output_name}${_shlib_extension}" ]
+        }
+      }
+    }
+  }
+}
+
+# ==============================================================================
+# COMPONENT SETUP
+# ==============================================================================
+
+# Defines a component, which equates to a shared_library when
+# is_component_build == true and a static_library otherwise.
+#
+# Use static libraries for the static build rather than source sets because
+# many of of our test binaries link many large dependencies but often don't
+# use large portions of them. The static libraries are much more efficient to
+# link in this situation since only the necessary object files are linked.
+#
+# The invoker can override the type of the target in the non-component-build
+# case by setting static_component_type to either "source_set" or
+# "static_library". If unset, the default will be used.
+template("component") {
+  if (is_component_build) {
+    _component_mode = "shared_library"
+  } else if (defined(invoker.static_component_type)) {
+    assert(invoker.static_component_type == "static_library" ||
+           invoker.static_component_type == "source_set")
+    _component_mode = invoker.static_component_type
+  } else if (!defined(invoker.sources) || invoker.sources == []) {
+    # When there are no sources defined, use a source set to avoid creating
+    # an empty static library (which generally don't work).
+    _component_mode = "source_set"
+  } else {
+    _component_mode = "static_library"
+  }
+  target(_component_mode, target_name) {
+    forward_variables_from(invoker, TESTONLY_AND_VISIBILITY)
+    forward_variables_from(invoker, "*", TESTONLY_AND_VISIBILITY)
+  }
+}
+
+# Component defaults
+# Set a variable since we also want to make this available
+# to mixed_component.gni
+if (is_component_build) {
+  default_component_configs = default_shared_library_configs
+  if (is_android) {
+    default_component_configs -=
+        [ "//build/config/android:hide_all_but_jni_onload" ]
+  }
+} else {
+  default_component_configs = default_compiler_configs
+}
+
+set_defaults("component") {
+  configs = default_component_configs
+}
diff --git a/build_overrides/build.gni b/build_overrides/build.gni
index bb15253..446fa89 100644
--- a/build_overrides/build.gni
+++ b/build_overrides/build.gni
@@ -1,15 +1,7 @@
-# Copyright 2016 The Chromium Authors. All rights reserved.
+# Copyright 2016 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-# See https://bugs.chromium.org/p/webrtc/issues/detail?id=5453.
-# Some WebRTC targets require the 10.7 deployment version of the Mac SDK
-# and a 10.11 min SDK, but those targets are only used in non-Chromium
-# builds. We can remove this when Chromium drops 10.6 support and also
-# requires 10.7.
-mac_sdk_min_build_override = "10.10"
-mac_deployment_target_build_override = "10.7"
-
 # Variable that can be used to support multiple build scenarios, like having
 # Chromium specific targets in a client project's GN file etc.
 build_with_chromium = false
@@ -22,14 +14,14 @@
 # PDFium builds don't support building java targets.
 enable_java_templates = false
 
+# Enables assertions on safety checks in libc++.
+enable_safe_libcxx = true
+
 # Whether to use the neon FPU instruction set or not.
 if (current_cpu == "arm") {
   arm_use_neon = true
 }
 
-# PDFium builds don't use Chromium's third_party/binutils.
-linux_use_bundled_binutils_override = false
-
 # PDFium just uses the Chromium suppression files for now.
 asan_suppressions_file = "//build/sanitizers/asan_suppressions.cc"
 lsan_suppressions_file = "//build/sanitizers/lsan_suppressions.cc"
@@ -47,6 +39,10 @@
   # 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..0cf7009
--- /dev/null
+++ b/build_overrides/partition_alloc.gni
@@ -0,0 +1,11 @@
+# Copyright 2022 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# See base/allocator/partition_allocator/external_builds.md
+use_partition_alloc_as_malloc_default = false
+enable_mte_checked_ptr_support_default = false
+enable_backup_ref_ptr_support_default = false
+put_ref_count_in_previous_slot_default = false
+enable_backup_ref_ptr_slow_checks_default = false
+enable_dangling_raw_ptr_checks_default = false
diff --git a/build_overrides/pdfium.gni b/build_overrides/pdfium.gni
index 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..f3171c7 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 <stdint.h>
+
 #include "third_party/base/span.h"
 
-constexpr int32_t kRC4ContextPermutationLength = 256;
 struct CRYPT_rc4_context {
+  static constexpr int32_t kPermutationLength = 256;
+
   int32_t x;
   int32_t y;
-  int32_t m[kRC4ContextPermutationLength];
+  int32_t m[kPermutationLength];
 };
 
-#define MAX_NR 14
-#define MAX_NB 8
 struct CRYPT_aes_context {
+  static constexpr int kMaxNb = 8;
+  static constexpr int kMaxNr = 14;
+  static constexpr int kSchedSize = (kMaxNr + 1) * kMaxNb;
+
   int Nb;
   int Nr;
-  unsigned int keysched[(MAX_NR + 1) * MAX_NB];
-  unsigned int invkeysched[(MAX_NR + 1) * MAX_NB];
-  unsigned int iv[MAX_NB];
+  unsigned int keysched[kSchedSize];
+  unsigned int invkeysched[kSchedSize];
+  unsigned int iv[kMaxNb];
 };
 
 struct CRYPT_md5_context {
@@ -54,8 +58,7 @@
 
 void CRYPT_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..079459a 100644
--- a/core/fpdfapi/cmaps/fpdf_cmaps.h
+++ b/core/fpdfapi/cmaps/fpdf_cmaps.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,31 +9,30 @@
 
 #include <stdint.h>
 
-#include "core/fxcrt/fx_string.h"
-#include "third_party/base/span.h"
+namespace fxcmap {
 
-struct FXCMAP_DWordCIDMap {
+struct DWordCIDMap {
   uint16_t m_HiWord;
   uint16_t m_LoWordLow;
   uint16_t m_LoWordHigh;
   uint16_t m_CID;
 };
 
-struct FXCMAP_CMap {
-  enum MapType : uint8_t { Single, Range };
+struct CMap {
+  enum class Type : bool { kSingle, kRange };
 
-  const char* m_Name;                     // Raw, POD struct.
-  const uint16_t* m_pWordMap;             // Raw, POD struct.
-  const FXCMAP_DWordCIDMap* m_pDWordMap;  // Raw, POD struct.
+  const char* m_Name;              // Raw, POD struct.
+  const uint16_t* m_pWordMap;      // Raw, POD struct.
+  const DWordCIDMap* m_pDWordMap;  // Raw, POD struct.
   uint16_t m_WordCount;
   uint16_t m_DWordCount;
-  MapType m_WordMapType;
+  Type m_WordMapType;
   int8_t m_UseOffset;
 };
 
-const FXCMAP_CMap* FindEmbeddedCMap(pdfium::span<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..721dff8 100644
--- a/core/fpdfapi/edit/BUILD.gn
+++ b/core/fpdfapi/edit/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -18,7 +18,10 @@
     "cpdf_stringarchivestream.cpp",
     "cpdf_stringarchivestream.h",
   ]
-  configs += [ "../../../:pdfium_core_config" ]
+  configs += [
+    "../../../:pdfium_strict_config",
+    "../../../:pdfium_noshorten_config",
+  ]
   deps = [
     "../../../constants",
     "../../../third_party:skia_shared",
@@ -37,7 +40,9 @@
     "../../fxge",
     "../font",
     "../page",
+    "../page:unit_test_support",
     "../parser",
+    "../parser:unit_test_support",
     "../render",
   ]
   pdfium_root_dir = "../../../"
diff --git a/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp b/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
index 28165b1..af6a169 100644
--- a/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
+++ b/core/fpdfapi/edit/cpdf_contentstream_write_utils.cpp
@@ -1,9 +1,11 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/edit/cpdf_contentstream_write_utils.h"
 
+#include <ostream>
+
 #include "third_party/skia_shared/SkFloatToDecimal.h"
 
 std::ostream& WriteFloat(std::ostream& stream, float value) {
@@ -13,18 +15,26 @@
   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..8717028 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/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
+#include "third_party/base/span.h"
 
 namespace {
 
+// Key: The resource type.
+// Value: The resource names of a given type.
+using ResourcesMap = std::map<ByteString, std::set<ByteString>>;
+
 bool GetColor(const CPDF_Color* pColor, float* rgb) {
   int intRGB[3];
   if (!pColor || !pColor->IsColorSpaceRGB() ||
@@ -51,6 +63,68 @@
   return true;
 }
 
+void RecordPageObjectResourceUsage(const CPDF_PageObject* page_object,
+                                   ResourcesMap& seen_resources) {
+  const ByteString& resource_name = page_object->GetResourceName();
+  if (!resource_name.IsEmpty()) {
+    switch (page_object->GetType()) {
+      case CPDF_PageObject::Type::kText:
+        seen_resources["Font"].insert(resource_name);
+        break;
+      case CPDF_PageObject::Type::kImage:
+      case CPDF_PageObject::Type::kForm:
+        seen_resources["XObject"].insert(resource_name);
+        break;
+      case CPDF_PageObject::Type::kPath:
+        break;
+      case CPDF_PageObject::Type::kShading:
+        break;
+    }
+  }
+  const ByteString& graphics_resource_name =
+      page_object->GetGraphicsResourceName();
+  if (!graphics_resource_name.IsEmpty()) {
+    seen_resources["ExtGState"].insert(graphics_resource_name);
+  }
+}
+
+void RemoveUnusedResources(RetainPtr<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 +136,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 +164,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 +191,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 +216,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 +308,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 +324,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
@@ -282,7 +375,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 +384,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 +397,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 +457,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 +523,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,12 +543,35 @@
   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 ";
 
+  const CPDF_ClipPath& clip_path = pPageObj->m_ClipPath;
+  if (clip_path.HasRef()) {
+    for (size_t i = 0; i < clip_path.GetPathCount(); ++i) {
+      CPDF_Path path = clip_path.GetPath(i);
+      ProcessPathPoints(buf, &path);
+      switch (clip_path.GetClipType(i)) {
+        case CFX_FillRenderOptions::FillType::kWinding:
+          *buf << " W ";
+          break;
+        case CFX_FillRenderOptions::FillType::kEvenOdd:
+          *buf << " W* ";
+          break;
+        case CFX_FillRenderOptions::FillType::kNoFill:
+          NOTREACHED();
+          break;
+      }
+
+      // Use a no-op path-painting operator to terminate the path without
+      // causing any marks to be placed on the page.
+      *buf << "n ";
+    }
+  }
+
   GraphicsData graphD;
   graphD.fillAlpha = pPageObj->m_GeneralState.GetFillAlpha();
   graphD.strokeAlpha = pPageObj->m_GeneralState.GetStrokeAlpha();
@@ -432,9 +582,10 @@
   }
 
   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 +598,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->SetGraphicsResourceName(name);
+    m_pObjHolder->GraphicsMapInsert(graphD, name);
   }
   *buf << "/" << PDF_NameEncode(name) << " gs ";
 }
 
 void CPDF_PageContentGenerator::ProcessDefaultGraphics(
-    std::ostringstream* buf) {
+    fxcrt::ostringstream* buf) {
   *buf << "0 0 0 RG 0 0 0 rg 1 w "
-       << static_cast<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 +620,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 +664,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 +681,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..4d9f7f2 100644
--- a/core/fpdfapi/edit/cpdf_stringarchivestream.cpp
+++ b/core/fpdfapi/edit/cpdf_stringarchivestream.cpp
@@ -1,35 +1,24 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/edit/cpdf_stringarchivestream.h"
 
-CPDF_StringArchiveStream::CPDF_StringArchiveStream(std::ostringstream* stream)
+#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;
 }
 
-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..1306343 100644
--- a/core/fpdfapi/edit/cpdf_stringarchivestream.h
+++ b/core/fpdfapi/edit/cpdf_stringarchivestream.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,21 +6,19 @@
 #define CORE_FPDFAPI_EDIT_CPDF_STRINGARCHIVESTREAM_H_
 
 #include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/fx_string_wrappers.h"
 
 class CPDF_StringArchiveStream final : public IFX_ArchiveStream {
  public:
-  explicit CPDF_StringArchiveStream(std::ostringstream* stream);
+  explicit CPDF_StringArchiveStream(fxcrt::ostringstream* stream);
   ~CPDF_StringArchiveStream() override;
 
-  // IFX_ArchiveStream
-  bool WriteByte(uint8_t byte) override;
-  bool WriteDWord(uint32_t i) override;
+  // IFX_ArchiveStream:
+  bool WriteBlock(pdfium::span<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_;
+  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..ae5a841 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 {
 
@@ -50,61 +53,63 @@
 CFX_CTTGSUBTable::~CFX_CTTGSUBTable() = default;
 
 bool CFX_CTTGSUBTable::LoadGSUBTable(FT_Bytes gsub) {
-  if ((gsub[0] << 24u | gsub[1] << 16u | gsub[2] << 8u | gsub[3]) != 0x00010000)
+  if (FXSYS_UINT32_GET_MSBFIRST(gsub) != 0x00010000)
     return false;
 
-  return Parse(&gsub[gsub[4] << 8 | gsub[5]], &gsub[gsub[6] << 8 | gsub[7]],
-               &gsub[gsub[8] << 8 | gsub[9]]);
+  return Parse(&gsub[FXSYS_UINT16_GET_MSBFIRST(gsub + 4)],
+               &gsub[FXSYS_UINT16_GET_MSBFIRST(gsub + 6)],
+               &gsub[FXSYS_UINT16_GET_MSBFIRST(gsub + 8)]);
 }
 
 uint32_t CFX_CTTGSUBTable::GetVerticalGlyph(uint32_t glyphnum) const {
-  uint32_t vglyphnum = 0;
   for (uint32_t item : m_featureSet) {
-    if (GetVerticalGlyphSub(FeatureList[item], glyphnum, &vglyphnum))
-      break;
+    absl::optional<uint32_t> result =
+        GetVerticalGlyphSub(FeatureList[item], glyphnum);
+    if (result.has_value())
+      return result.value();
   }
-  return vglyphnum;
+  return 0;
 }
 
-bool CFX_CTTGSUBTable::GetVerticalGlyphSub(const TFeatureRecord& feature,
-                                           uint32_t glyphnum,
-                                           uint32_t* vglyphnum) const {
+absl::optional<uint32_t> CFX_CTTGSUBTable::GetVerticalGlyphSub(
+    const TFeatureRecord& feature,
+    uint32_t glyphnum) const {
   for (int index : feature.LookupListIndices) {
-    if (!pdfium::IndexInBounds(LookupList, index))
+    if (!fxcrt::IndexInBounds(LookupList, index))
       continue;
-    if (LookupList[index].LookupType == 1 &&
-        GetVerticalGlyphSub2(LookupList[index], glyphnum, vglyphnum)) {
-      return true;
-    }
+    if (LookupList[index].LookupType != 1)
+      continue;
+    absl::optional<uint32_t> result =
+        GetVerticalGlyphSub2(LookupList[index], glyphnum);
+    if (result.has_value())
+      return result.value();
   }
-  return false;
+  return absl::nullopt;
 }
 
-bool CFX_CTTGSUBTable::GetVerticalGlyphSub2(const TLookup& lookup,
-                                            uint32_t glyphnum,
-                                            uint32_t* vglyphnum) const {
+absl::optional<uint32_t> CFX_CTTGSUBTable::GetVerticalGlyphSub2(
+    const TLookup& lookup,
+    uint32_t glyphnum) const {
   for (const auto& subTable : lookup.SubTables) {
     switch (subTable->SubstFormat) {
       case 1: {
         auto* tbl1 = static_cast<TSubTable1*>(subTable.get());
         if (GetCoverageIndex(tbl1->Coverage.get(), glyphnum) >= 0) {
-          *vglyphnum = glyphnum + tbl1->DeltaGlyphID;
-          return true;
+          return glyphnum + tbl1->DeltaGlyphID;
         }
         break;
       }
       case 2: {
         auto* tbl2 = static_cast<TSubTable2*>(subTable.get());
         int index = GetCoverageIndex(tbl2->Coverage.get(), glyphnum);
-        if (pdfium::IndexInBounds(tbl2->Substitutes, index)) {
-          *vglyphnum = tbl2->Substitutes[index];
-          return true;
+        if (fxcrt::IndexInBounds(tbl2->Substitutes, index)) {
+          return tbl2->Substitutes[index];
         }
         break;
       }
     }
   }
-  return false;
+  return absl::nullopt;
 }
 
 int CFX_CTTGSUBTable::GetCoverageIndex(TCoverageFormatBase* Coverage,
@@ -145,25 +150,25 @@
 }
 
 int16_t CFX_CTTGSUBTable::GetInt16(FT_Bytes& p) const {
-  uint16_t ret = p[0] << 8 | p[1];
+  uint16_t ret = FXSYS_UINT16_GET_MSBFIRST(p);
   p += 2;
   return *(int16_t*)&ret;
 }
 
 uint16_t CFX_CTTGSUBTable::GetUInt16(FT_Bytes& p) const {
-  uint16_t ret = p[0] << 8 | p[1];
+  uint16_t ret = FXSYS_UINT16_GET_MSBFIRST(p);
   p += 2;
   return ret;
 }
 
 int32_t CFX_CTTGSUBTable::GetInt32(FT_Bytes& p) const {
-  uint32_t ret = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+  uint32_t ret = FXSYS_UINT32_GET_MSBFIRST(p);
   p += 4;
   return *(int32_t*)&ret;
 }
 
 uint32_t CFX_CTTGSUBTable::GetUInt32(FT_Bytes& p) const {
-  uint32_t ret = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+  uint32_t ret = FXSYS_UINT32_GET_MSBFIRST(p);
   p += 4;
   return ret;
 }
@@ -200,7 +205,7 @@
   FT_Bytes sp = raw;
   rec->LookupOrder = GetUInt16(sp);
   rec->ReqFeatureIndex = GetUInt16(sp);
-  rec->FeatureIndices = std::vector<uint16_t>(GetUInt16(sp));
+  rec->FeatureIndices = DataVector<uint16_t>(GetUInt16(sp));
   for (auto& element : rec->FeatureIndices)
     element = GetUInt16(sp);
 }
@@ -217,7 +222,7 @@
 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));
+  rec->LookupListIndices = DataVector<uint16_t>(GetUInt16(sp));
   for (auto& listIndex : rec->LookupListIndices)
     listIndex = GetUInt16(sp);
 }
@@ -238,127 +243,117 @@
     return;
 
   for (auto& subTable : rec->SubTables)
-    ParseSingleSubst(&raw[GetUInt16(sp)], &subTable);
+    subTable = ParseSingleSubst(&raw[GetUInt16(sp)]);
 }
 
 std::unique_ptr<CFX_CTTGSUBTable::TCoverageFormatBase>
 CFX_CTTGSUBTable::ParseCoverage(FT_Bytes raw) {
   FT_Bytes sp = raw;
   uint16_t format = GetUInt16(sp);
-  if (format == 1) {
-    auto rec = pdfium::MakeUnique<TCoverageFormat1>();
-    ParseCoverageFormat1(raw, rec.get());
-    return std::move(rec);
-  }
-  if (format == 2) {
-    auto rec = pdfium::MakeUnique<TCoverageFormat2>();
-    ParseCoverageFormat2(raw, rec.get());
-    return std::move(rec);
-  }
+  if (format == 1)
+    return ParseCoverageFormat1(raw);
+  if (format == 2)
+    return ParseCoverageFormat2(raw);
   return nullptr;
 }
 
-void CFX_CTTGSUBTable::ParseCoverageFormat1(FT_Bytes raw,
-                                            TCoverageFormat1* rec) {
+std::unique_ptr<CFX_CTTGSUBTable::TCoverageFormat1>
+CFX_CTTGSUBTable::ParseCoverageFormat1(FT_Bytes raw) {
   FT_Bytes sp = raw;
   (void)GetUInt16(sp);
-  rec->GlyphArray = std::vector<uint16_t>(GetUInt16(sp));
+  auto rec = std::make_unique<TCoverageFormat1>(GetUInt16(sp));
   for (auto& glyph : rec->GlyphArray)
     glyph = GetUInt16(sp);
+  return rec;
 }
 
-void CFX_CTTGSUBTable::ParseCoverageFormat2(FT_Bytes raw,
-                                            TCoverageFormat2* rec) {
+std::unique_ptr<CFX_CTTGSUBTable::TCoverageFormat2>
+CFX_CTTGSUBTable::ParseCoverageFormat2(FT_Bytes raw) {
   FT_Bytes sp = raw;
   (void)GetUInt16(sp);
-  rec->RangeRecords = std::vector<TRangeRecord>(GetUInt16(sp));
+  auto rec = std::make_unique<TCoverageFormat2>(GetUInt16(sp));
   for (auto& rangeRec : rec->RangeRecords) {
     rangeRec.Start = GetUInt16(sp);
     rangeRec.End = GetUInt16(sp);
     rangeRec.StartCoverageIndex = GetUInt16(sp);
   }
+  return rec;
 }
 
-void CFX_CTTGSUBTable::ParseSingleSubst(FT_Bytes raw,
-                                        std::unique_ptr<TSubTableBase>* rec) {
+std::unique_ptr<CFX_CTTGSUBTable::TSubTableBase>
+CFX_CTTGSUBTable::ParseSingleSubst(FT_Bytes raw) {
   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;
-  }
+  uint16_t format = GetUInt16(sp);
+  if (format == 1)
+    return ParseSingleSubstFormat1(raw);
+  if (format == 2)
+    return ParseSingleSubstFormat2(raw);
+  return nullptr;
 }
 
-void CFX_CTTGSUBTable::ParseSingleSubstFormat1(FT_Bytes raw, TSubTable1* rec) {
+std::unique_ptr<CFX_CTTGSUBTable::TSubTable1>
+CFX_CTTGSUBTable::ParseSingleSubstFormat1(FT_Bytes raw) {
   FT_Bytes sp = raw;
   GetUInt16(sp);
   uint16_t offset = GetUInt16(sp);
+  auto rec = std::make_unique<TSubTable1>();
   rec->Coverage = ParseCoverage(&raw[offset]);
   rec->DeltaGlyphID = GetInt16(sp);
+  return rec;
 }
 
-void CFX_CTTGSUBTable::ParseSingleSubstFormat2(FT_Bytes raw, TSubTable2* rec) {
+std::unique_ptr<CFX_CTTGSUBTable::TSubTable2>
+CFX_CTTGSUBTable::ParseSingleSubstFormat2(FT_Bytes raw) {
   FT_Bytes sp = raw;
   (void)GetUInt16(sp);
   uint16_t offset = GetUInt16(sp);
+  auto rec = std::make_unique<TSubTable2>();
   rec->Coverage = ParseCoverage(&raw[offset]);
-  rec->Substitutes = std::vector<uint16_t>(GetUInt16(sp));
+  rec->Substitutes = DataVector<uint16_t>(GetUInt16(sp));
   for (auto& substitute : rec->Substitutes)
     substitute = GetUInt16(sp);
+  return rec;
 }
 
-CFX_CTTGSUBTable::TLangSysRecord::TLangSysRecord()
-    : LangSysTag(0), LookupOrder(0), ReqFeatureIndex(0) {}
+CFX_CTTGSUBTable::TLangSysRecord::TLangSysRecord() = default;
 
-CFX_CTTGSUBTable::TLangSysRecord::~TLangSysRecord() {}
+CFX_CTTGSUBTable::TLangSysRecord::~TLangSysRecord() = default;
 
-CFX_CTTGSUBTable::TScriptRecord::TScriptRecord()
-    : ScriptTag(0), DefaultLangSys(0) {}
+CFX_CTTGSUBTable::TScriptRecord::TScriptRecord() = default;
 
-CFX_CTTGSUBTable::TScriptRecord::~TScriptRecord() {}
+CFX_CTTGSUBTable::TScriptRecord::~TScriptRecord() = default;
 
-CFX_CTTGSUBTable::TFeatureRecord::TFeatureRecord()
-    : FeatureTag(0), FeatureParams(0) {}
+CFX_CTTGSUBTable::TFeatureRecord::TFeatureRecord() = default;
 
-CFX_CTTGSUBTable::TFeatureRecord::~TFeatureRecord() {}
+CFX_CTTGSUBTable::TFeatureRecord::~TFeatureRecord() = default;
 
-CFX_CTTGSUBTable::TRangeRecord::TRangeRecord()
-    : Start(0), End(0), StartCoverageIndex(0) {}
+CFX_CTTGSUBTable::TRangeRecord::TRangeRecord() = default;
 
-CFX_CTTGSUBTable::TCoverageFormat1::TCoverageFormat1() {
-  CoverageFormat = 1;
-}
+CFX_CTTGSUBTable::TCoverageFormat1::TCoverageFormat1(size_t initial_size)
+    : TCoverageFormatBase(1), GlyphArray(initial_size) {}
 
-CFX_CTTGSUBTable::TCoverageFormat1::~TCoverageFormat1() {}
+CFX_CTTGSUBTable::TCoverageFormat1::~TCoverageFormat1() = default;
 
-CFX_CTTGSUBTable::TCoverageFormat2::TCoverageFormat2() {
-  CoverageFormat = 2;
-}
+CFX_CTTGSUBTable::TCoverageFormat2::TCoverageFormat2(size_t initial_size)
+    : TCoverageFormatBase(2), RangeRecords(initial_size) {}
 
-CFX_CTTGSUBTable::TCoverageFormat2::~TCoverageFormat2() {}
+CFX_CTTGSUBTable::TCoverageFormat2::~TCoverageFormat2() = default;
 
-CFX_CTTGSUBTable::TSubTableBase::TSubTableBase() {}
+CFX_CTTGSUBTable::TDevice::TDevice() = default;
 
-CFX_CTTGSUBTable::TSubTableBase::~TSubTableBase() {}
+CFX_CTTGSUBTable::TSubTableBase::TSubTableBase(uint16_t format)
+    : SubstFormat(format) {}
 
-CFX_CTTGSUBTable::TSubTable1::TSubTable1() {
-  SubstFormat = 1;
-}
+CFX_CTTGSUBTable::TSubTableBase::~TSubTableBase() = default;
 
-CFX_CTTGSUBTable::TSubTable1::~TSubTable1() {}
+CFX_CTTGSUBTable::TSubTable1::TSubTable1() : TSubTableBase(1) {}
 
-CFX_CTTGSUBTable::TSubTable2::TSubTable2() {
-  SubstFormat = 2;
-}
+CFX_CTTGSUBTable::TSubTable1::~TSubTable1() = default;
 
-CFX_CTTGSUBTable::TSubTable2::~TSubTable2() {}
+CFX_CTTGSUBTable::TSubTable2::TSubTable2() : TSubTableBase(2) {}
 
-CFX_CTTGSUBTable::TLookup::TLookup() : LookupType(0), LookupFlag(0) {}
+CFX_CTTGSUBTable::TSubTable2::~TSubTable2() = default;
 
-CFX_CTTGSUBTable::TLookup::~TLookup() {}
+CFX_CTTGSUBTable::TLookup::TLookup() = default;
+
+CFX_CTTGSUBTable::TLookup::~TLookup() = default;
diff --git a/core/fpdfapi/font/cfx_cttgsubtable.h b/core/fpdfapi/font/cfx_cttgsubtable.h
index 54e16b4..0555333 100644
--- a/core/fpdfapi/font/cfx_cttgsubtable.h
+++ b/core/fpdfapi/font/cfx_cttgsubtable.h
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,7 +13,9 @@
 #include <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"
 
 class CFX_CTTGSUBTable {
  public:
@@ -27,18 +29,18 @@
     TLangSysRecord();
     ~TLangSysRecord();
 
-    uint32_t LangSysTag;
-    uint16_t LookupOrder;
-    uint16_t ReqFeatureIndex;
-    std::vector<uint16_t> FeatureIndices;
+    uint32_t LangSysTag = 0;
+    uint16_t LookupOrder = 0;
+    uint16_t ReqFeatureIndex = 0;
+    DataVector<uint16_t> FeatureIndices;
   };
 
   struct TScriptRecord {
     TScriptRecord();
     ~TScriptRecord();
 
-    uint32_t ScriptTag;
-    uint16_t DefaultLangSys;
+    uint32_t ScriptTag = 0;
+    uint16_t DefaultLangSys = 0;
     std::vector<TLangSysRecord> LangSysRecords;
   };
 
@@ -46,74 +48,76 @@
     TFeatureRecord();
     ~TFeatureRecord();
 
-    uint32_t FeatureTag;
-    uint16_t FeatureParams;
-    std::vector<uint16_t> LookupListIndices;
+    uint32_t FeatureTag = 0;
+    uint16_t FeatureParams = 0;
+    DataVector<uint16_t> LookupListIndices;
   };
 
   struct TRangeRecord {
     TRangeRecord();
 
-    uint16_t Start;
-    uint16_t End;
-    uint16_t StartCoverageIndex;
+    uint16_t Start = 0;
+    uint16_t End = 0;
+    uint16_t StartCoverageIndex = 0;
   };
 
   struct TCoverageFormatBase {
+    explicit TCoverageFormatBase(uint16_t format) : CoverageFormat(format) {}
     virtual ~TCoverageFormatBase() = default;
-    uint16_t CoverageFormat;
+
+    const uint16_t CoverageFormat;
   };
 
   struct TCoverageFormat1 final : public TCoverageFormatBase {
-    TCoverageFormat1();
+    explicit TCoverageFormat1(size_t initial_size);
     ~TCoverageFormat1() override;
 
-    std::vector<uint16_t> GlyphArray;
+    DataVector<uint16_t> GlyphArray;
   };
 
   struct TCoverageFormat2 final : public TCoverageFormatBase {
-    TCoverageFormat2();
+    explicit TCoverageFormat2(size_t initial_size);
     ~TCoverageFormat2() override;
 
     std::vector<TRangeRecord> RangeRecords;
   };
 
   struct TDevice {
-    TDevice() : StartSize(0), EndSize(0), DeltaFormat(0) {}
+    TDevice();
 
-    uint16_t StartSize;
-    uint16_t EndSize;
-    uint16_t DeltaFormat;
+    uint16_t StartSize = 0;
+    uint16_t EndSize = 0;
+    uint16_t DeltaFormat = 0;
   };
 
   struct TSubTableBase {
-    TSubTableBase();
+    explicit TSubTableBase(uint16_t format);
     virtual ~TSubTableBase();
 
+    const uint16_t SubstFormat;
     std::unique_ptr<TCoverageFormatBase> Coverage;
-    uint16_t SubstFormat;
   };
 
   struct TSubTable1 final : public TSubTableBase {
     TSubTable1();
     ~TSubTable1() override;
 
-    int16_t DeltaGlyphID;
+    int16_t DeltaGlyphID = 0;
   };
 
   struct TSubTable2 final : public TSubTableBase {
     TSubTable2();
     ~TSubTable2() override;
 
-    std::vector<uint16_t> Substitutes;
+    DataVector<uint16_t> Substitutes;
   };
 
   struct TLookup {
     TLookup();
     ~TLookup();
 
-    uint16_t LookupType;
-    uint16_t LookupFlag;
+    uint16_t LookupType = 0;
+    uint16_t LookupFlag = 0;
     std::vector<std::unique_ptr<TSubTableBase>> SubTables;
   };
 
@@ -127,18 +131,16 @@
   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);
+  std::unique_ptr<TCoverageFormat1> ParseCoverageFormat1(FT_Bytes raw);
+  std::unique_ptr<TCoverageFormat2> ParseCoverageFormat2(FT_Bytes raw);
+  std::unique_ptr<TSubTableBase> ParseSingleSubst(FT_Bytes raw);
+  std::unique_ptr<TSubTable1> ParseSingleSubstFormat1(FT_Bytes raw);
+  std::unique_ptr<TSubTable2> ParseSingleSubstFormat2(FT_Bytes raw);
 
-  bool GetVerticalGlyphSub(const TFeatureRecord& feature,
-                           uint32_t glyphnum,
-                           uint32_t* vglyphnum) const;
-  bool GetVerticalGlyphSub2(const TLookup& lookup,
-                            uint32_t glyphnum,
-                            uint32_t* vglyphnum) const;
+  absl::optional<uint32_t> GetVerticalGlyphSub(const TFeatureRecord& feature,
+                                               uint32_t glyphnum) const;
+  absl::optional<uint32_t> GetVerticalGlyphSub2(const TLookup& lookup,
+                                                uint32_t glyphnum) const;
   int GetCoverageIndex(TCoverageFormatBase* Coverage, uint32_t g) const;
 
   uint8_t GetUInt8(FT_Bytes& p) const;
diff --git a/core/fpdfapi/font/cfx_stockfontarray.cpp b/core/fpdfapi/font/cfx_stockfontarray.cpp
index a8d8597..b6793ff 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/notreached.h"
 
 CFX_StockFontArray::CFX_StockFontArray() = default;
 
 CFX_StockFontArray::~CFX_StockFontArray() {
-  for (size_t i = 0; i < FX_ArraySize(m_StockFonts); ++i) {
+  for (size_t i = 0; i < std::size(m_StockFonts); ++i) {
     if (m_StockFonts[i]) {
-      RetainPtr<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,14 @@
 
 RetainPtr<CPDF_Font> CFX_StockFontArray::GetFont(
     CFX_FontMapper::StandardFont index) const {
-  if (index < FX_ArraySize(m_StockFonts))
+  if (index < std::size(m_StockFonts))
     return m_StockFonts[index];
   NOTREACHED();
   return nullptr;
 }
 
 void CFX_StockFontArray::SetFont(CFX_FontMapper::StandardFont index,
-                                 const RetainPtr<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..5c18493 100644
--- a/core/fpdfapi/font/cpdf_cid2unicodemap.h
+++ b/core/fpdfapi/font/cpdf_cid2unicodemap.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/core/fpdfapi/font/cpdf_cidfont.cpp b/core/fpdfapi/font/cpdf_cidfont.cpp
index 0c02b9e..6e0e665 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/fx_codepage.h"
+#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_unicode.h"
+#include "core/fxcrt/stl_util.h"
 #include "core/fxge/fx_font.h"
-#include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/cxx17_backports.h"
 #include "third_party/base/span.h"
-#include "third_party/base/stl_util.h"
 
 namespace {
 
-const uint16_t g_CharsetCPs[CIDSET_NUM_SETS] = {0, 936, 950, 932, 949, 1200};
+constexpr FX_CodePage kCharsetCodePages[CIDSET_NUM_SETS] = {
+    FX_CodePage::kDefANSI,
+    FX_CodePage::kChineseSimplified,
+    FX_CodePage::kChineseTraditional,
+    FX_CodePage::kShiftJIS,
+    FX_CodePage::kHangul,
+    FX_CodePage::kUTF16LE};
 
-const struct CIDTransform {
+struct CIDTransform {
   uint16_t cid;
   uint8_t a;
   uint8_t b;
@@ -39,7 +52,9 @@
   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>(pdfium::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,603 @@
   }
 }
 
+}  // 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');
+  if (!m_Font.GetSubData()) {
+    unsigned long length = 0;
+    int error = FT_Load_Sfnt_Table(face, kGsubTag, 0, nullptr, &length);
+    if (!error)
+      m_Font.AllocSubData(length);
+  }
+  int error =
+      FT_Load_Sfnt_Table(face, kGsubTag, 0, m_Font.GetSubData(), nullptr);
+  if (error || !m_Font.GetSubData())
+    return index;
+
+  m_pTTGSUBTable = std::make_unique<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 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 +879,14 @@
   m_bAnsiWidthsFixed = true;
 }
 
-const uint8_t* CPDF_CIDFont::GetCIDTransform(uint16_t CID) const {
+const uint8_t* CPDF_CIDFont::GetCIDTransform(uint16_t cid) const {
   if (m_Charset != CIDSET_JAPAN1 || m_pFontFile)
     return nullptr;
 
-  const auto* pEnd = g_Japan1_VertCIDs + FX_ArraySize(g_Japan1_VertCIDs);
+  const auto* pEnd = kJapan1VerticalCIDs + std::size(kJapan1VerticalCIDs);
   const auto* pTransform = std::lower_bound(
-      g_Japan1_VertCIDs, pEnd, CID,
+      kJapan1VerticalCIDs, pEnd, cid,
       [](const CIDTransform& entry, uint16_t cid) { return entry.cid < cid; });
-  return (pTransform < pEnd && CID == pTransform->cid) ? &pTransform->a
+  return (pTransform < pEnd && cid == pTransform->cid) ? &pTransform->a
                                                        : nullptr;
 }
diff --git a/core/fpdfapi/font/cpdf_cidfont.h b/core/fpdfapi/font/cpdf_cidfont.h
index 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..cc186e8 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 "core/fxcrt/unowned_ptr.h"
 #include "third_party/base/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..a74cba9 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,53 +17,53 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_font.h"
+#include "core/fxge/freetype/fx_freetype.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 
 class CFX_DIBitmap;
-class CFX_SubstFont;
 class CPDF_CIDFont;
 class CPDF_Document;
-class CPDF_Object;
 class CPDF_TrueTypeFont;
 class CPDF_Type1Font;
 class CPDF_Type3Char;
 class CPDF_Type3Font;
 class CPDF_ToUnicodeMap;
+enum class FontEncoding;
 
 class CPDF_Font : public Retainable, public Observable {
  public:
   // Callback mechanism for Type3 fonts to get pixels from forms.
   class FormIface {
    public:
-    virtual ~FormIface() {}
+    virtual ~FormIface() = default;
 
     virtual void ParseContentForType3Char(CPDF_Type3Char* pChar) = 0;
     virtual bool HasPageObjects() const = 0;
     virtual CFX_FloatRect CalcBoundingBox() const = 0;
-    virtual Optional<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);
@@ -83,12 +85,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 +98,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 +114,48 @@
   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);
+
+  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 +166,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..1aa19d6 100644
--- a/core/fpdfapi/font/cpdf_fontglobals.cpp
+++ b/core/fpdfapi/font/cpdf_fontglobals.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,37 +6,47 @@
 
 #include "core/fpdfapi/font/cpdf_fontglobals.h"
 
+#include <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;
 }
 
@@ -66,38 +76,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..6b2e3bc 100644
--- a/core/fpdfapi/font/cpdf_fontglobals.h
+++ b/core/fpdfapi/font/cpdf_fontglobals.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,16 +7,18 @@
 #ifndef CORE_FPDFAPI_FONT_CPDF_FONTGLOBALS_H_
 #define CORE_FPDFAPI_FONT_CPDF_FONTGLOBALS_H_
 
+#include <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"
 
 class CFX_StockFontArray;
+class CPDF_Font;
 
 class CPDF_FontGlobals {
  public:
@@ -33,22 +35,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 +62,14 @@
   void LoadEmbeddedJapan1CMaps();
   void LoadEmbeddedKorea1CMaps();
 
-  CPDF_CMapManager m_CMapManager;
-  pdfium::span<const FXCMAP_CMap> m_EmbeddedCharsets[CIDSET_NUM_SETS];
+  std::map<ByteString, RetainPtr<const CPDF_CMap>> m_CMaps;
+  std::unique_ptr<CPDF_CID2UnicodeMap> m_CID2UnicodeMaps[CIDSET_NUM_SETS];
+  pdfium::span<const fxcmap::CMap> m_EmbeddedCharsets[CIDSET_NUM_SETS];
   pdfium::span<const uint16_t> m_EmbeddedToUnicodes[CIDSET_NUM_SETS];
-  std::map<CPDF_Document*, std::unique_ptr<CFX_StockFontArray>> m_StockMap;
+  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..3c6e73e 100644
--- a/core/fpdfapi/font/cpdf_tounicodemap.cpp
+++ b/core/fpdfapi/font/cpdf_tounicodemap.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 
 #include "core/fpdfapi/font/cpdf_tounicodemap.h"
 
+#include <set>
 #include <utility>
 
 #include "core/fpdfapi/font/cpdf_cid2unicodemap.h"
@@ -14,6 +15,7 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/containers/contains.h"
 #include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
@@ -37,56 +39,60 @@
 
 }  // 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 str) {
   size_t len = str.GetLength();
   if (len <= 2 || str[0] != '<' || str[len - 1] != '>')
-    return pdfium::nullopt;
+    return absl::nullopt;
 
   FX_SAFE_UINT32 code = 0;
   for (char c : str.Substr(1, len - 2)) {
     if (!FXSYS_IsHexDigit(c))
-      return pdfium::nullopt;
+      return absl::nullopt;
 
     code = code * 16 + FXSYS_HexCharToInt(c);
     if (!code.IsValid())
-      return pdfium::nullopt;
+      return absl::nullopt;
   }
-  return pdfium::Optional<uint32_t>(code.ValueOrDie());
+  return absl::optional<uint32_t>(code.ValueOrDie());
 }
 
 // static
@@ -113,12 +119,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 +142,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 +162,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 +181,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 +226,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..a074f45 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 str);
   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..86ac2b1 100644
--- a/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp
+++ b/core/fpdfapi/font/cpdf_tounicodemap_unittest.cpp
@@ -1,11 +1,14 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/font/cpdf_tounicodemap.h"
 
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/span.h"
 
 TEST(cpdf_tounicodemap, StringToCode) {
   EXPECT_THAT(CPDF_ToUnicodeMap::StringToCode("<0001>"), testing::Optional(1u));
@@ -47,3 +50,70 @@
   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..0a59b54 100644
--- a/core/fpdfapi/font/cpdf_truetypefont.cpp
+++ b/core/fpdfapi/font/cpdf_truetypefont.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,18 +6,37 @@
 
 #include "core/fpdfapi/font/cpdf_truetypefont.h"
 
+#include <algorithm>
+#include <utility>
+
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fxge/fx_font.h"
+#include "third_party/base/cxx17_backports.h"
 
 namespace {
 
-const uint8_t kPrefix[4] = {0x00, 0xf0, 0xf1, 0xf2};
+constexpr uint8_t kPrefix[4] = {0x00, 0xf0, 0xf1, 0xf2};
+
+uint16_t GetGlyphIndexForMSSymbol(FXFT_FaceRec* face, uint32_t charcode) {
+  for (uint8_t c : kPrefix) {
+    uint16_t unicode = c * 256 + charcode;
+    uint16_t val = FT_Get_Char_Index(face, unicode);
+    if (val)
+      return val;
+  }
+  return 0;
+}
+
+bool IsWinAnsiOrMacRomanEncoding(FontEncoding encoding) {
+  return encoding == FontEncoding::kWinAnsi ||
+         encoding == FontEncoding::kMacRoman;
+}
 
 }  // namespace
 
 CPDF_TrueTypeFont::CPDF_TrueTypeFont(CPDF_Document* pDocument,
-                                     CPDF_Dictionary* pFontDict)
-    : CPDF_SimpleFont(pDocument, pFontDict) {}
+                                     RetainPtr<CPDF_Dictionary> pFontDict)
+    : CPDF_SimpleFont(pDocument, std::move(pFontDict)) {}
 
 CPDF_TrueTypeFont::~CPDF_TrueTypeFont() = default;
 
@@ -42,86 +61,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 +105,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 +117,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..afa39ef 100644
--- a/core/fpdfapi/font/cpdf_type3char.h
+++ b/core/fpdfapi/font/cpdf_type3char.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,9 +12,9 @@
 
 #include "core/fpdfapi/font/cpdf_font.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "third_party/base/optional.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+#include "third_party/base/span.h"
 
 class CFX_DIBitmap;
 
@@ -27,15 +27,14 @@
   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..51b4adf 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,7 +107,7 @@
     "cpdf_transparency.h",
     "ipdf_page.h",
   ]
-  configs += [ "../../../:pdfium_core_config" ]
+  configs += [ "../../../:pdfium_strict_config" ]
   deps = [
     "../../../constants",
     "../../fxcodec",
@@ -109,20 +117,40 @@
     "../parser",
   ]
   allow_circular_includes_from = []
-  if (pdf_use_skia || pdf_use_skia_paths) {
+  if (pdf_use_skia) {
     allow_circular_includes_from += [ "../../fxge" ]
   }
   visibility = [ "../../../*" ]
 }
 
+source_set("unit_test_support") {
+  testonly = true
+  sources = [
+    "test_with_page_module.cpp",
+    "test_with_page_module.h",
+  ]
+  configs += [ "../../../:pdfium_strict_config" ]
+  deps = [
+    "../page",
+    "//testing/gtest",
+  ]
+}
+
 pdfium_unittest_source_set("unittests") {
   sources = [
+    "cpdf_colorspace_unittest.cpp",
     "cpdf_devicecs_unittest.cpp",
+    "cpdf_function_unittest.cpp",
+    "cpdf_pageimagecache_unittest.cpp",
     "cpdf_pageobjectholder_unittest.cpp",
     "cpdf_psengine_unittest.cpp",
     "cpdf_streamcontentparser_unittest.cpp",
     "cpdf_streamparser_unittest.cpp",
   ]
-  deps = [ ":page" ]
+  deps = [
+    ":page",
+    "../parser",
+    "../render",
+  ]
   pdfium_root_dir = "../../../"
 }
diff --git a/core/fpdfapi/page/cpdf_allstates.cpp b/core/fpdfapi/page/cpdf_allstates.cpp
index a12ce33..a4330db 100644
--- a/core/fpdfapi/page/cpdf_allstates.cpp
+++ b/core/fpdfapi/page/cpdf_allstates.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,9 +16,9 @@
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/cxx17_backports.h"
 
 CPDF_AllStates::CPDF_AllStates() = default;
 
@@ -26,6 +26,7 @@
 
 void CPDF_AllStates::Copy(const CPDF_AllStates& src) {
   CopyStates(src);
+  m_GraphicsResourceName = src.m_GraphicsResourceName;
   m_TextMatrix = src.m_TextMatrix;
   m_ParentMatrix = src.m_ParentMatrix;
   m_CTM = src.m_CTM;
@@ -43,17 +44,15 @@
   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,53 +69,52 @@
         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));
@@ -140,20 +138,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..63eb527 100644
--- a/core/fpdfapi/page/cpdf_allstates.h
+++ b/core/fpdfapi/page/cpdf_allstates.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,8 +8,8 @@
 #define CORE_FPDFAPI_PAGE_CPDF_ALLSTATES_H_
 
 #include "core/fpdfapi/page/cpdf_graphicstates.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 
 class CPDF_Array;
 class CPDF_Dictionary;
@@ -21,9 +21,11 @@
   ~CPDF_AllStates() override;
 
   void Copy(const CPDF_AllStates& src);
-  void ProcessExtGS(CPDF_Dictionary* pGS, CPDF_StreamContentParser* pParser);
+  void ProcessExtGS(const CPDF_Dictionary* pGS,
+                    CPDF_StreamContentParser* pParser);
   void SetLineDash(const CPDF_Array* pArray, float phase, float scale);
 
+  ByteString m_GraphicsResourceName;
   CFX_Matrix m_TextMatrix;
   CFX_Matrix m_CTM;
   CFX_Matrix m_ParentMatrix;
diff --git a/core/fpdfapi/page/cpdf_annotcontext.cpp b/core/fpdfapi/page/cpdf_annotcontext.cpp
index 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..d3e605b 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/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..9d94acd 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,25 @@
 #include "core/fpdfapi/parser/cpdf_name.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
-#include "core/fpdfapi/parser/cpdf_stream_acc.h"
-#include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcodec/fx_codec.h"
-#include "core/fxcodec/icc/iccmodule.h"
-#include "core/fxcrt/fx_memory.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_2d_size.h"
+#include "core/fxcrt/fx_memory_wrappers.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/maybe_owned.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/scoped_set_insertion.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/cxx17_backports.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
-const uint8_t g_sRGBSamples1[] = {
+constexpr uint8_t kSRGBSamples1[] = {
     0,   3,   6,   10,  13,  15,  18,  20,  22,  23,  25,  27,  28,  30,  31,
     32,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
     48,  49,  49,  50,  51,  52,  53,  53,  54,  55,  56,  56,  57,  58,  58,
@@ -53,7 +64,7 @@
     116, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 120,
 };
 
-const uint8_t g_sRGBSamples2[] = {
+constexpr uint8_t kSRGBSamples2[] = {
     120, 121, 122, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135,
     136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149,
     150, 151, 152, 153, 154, 155, 155, 156, 157, 158, 159, 159, 160, 161, 162,
@@ -79,7 +90,7 @@
 }
 
 void 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 +98,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 +107,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 +139,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 +170,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 +207,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 +236,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 +248,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;
 };
 
@@ -390,8 +376,8 @@
   colorComponent = pdfium::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 +426,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 +463,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 +493,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 +515,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 +560,7 @@
 }
 
 std::vector<float> CPDF_ColorSpace::CreateBufAndSetDefaultColor() const {
-  ASSERT(m_Family != PDFCS_PATTERN);
+  DCHECK(m_Family != Family::kPattern);
 
   float min;
   float max;
@@ -608,21 +584,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 +615,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 +748,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 +786,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 = pdfium::clamp(0.0f, *min, *max);
+      return;
+    }
   }
 
-  *min = m_Ranges[iComponent * 2 - 2];
-  *max = m_Ranges[iComponent * 2 - 1];
-  *value = pdfium::clamp(0.0f, *min, *max);
+  *min = 0.0f;
+  *max = 100.0f;
+  *value = 0.0f;
 }
 
 uint32_t CPDF_LabCS::v_Load(CPDF_Document* pDoc,
                             const CPDF_Array* pArray,
                             std::set<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 +858,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 +884,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 +919,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 +958,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 +990,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 +1007,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 +1029,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 +1041,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 +1064,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 +1105,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 +1178,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 +1209,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..23253c8 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"
 
-#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..ec2de6b 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/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..6b5cd62 100644
--- a/core/fpdfapi/page/cpdf_contentmarkitem.cpp
+++ b/core/fpdfapi/page/cpdf_contentmarkitem.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,28 +13,23 @@
 CPDF_ContentMarkItem::CPDF_ContentMarkItem(ByteString name)
     : m_MarkName(std::move(name)) {}
 
-CPDF_ContentMarkItem::~CPDF_ContentMarkItem() {}
+CPDF_ContentMarkItem::~CPDF_ContentMarkItem() = default;
 
-const CPDF_Dictionary* CPDF_ContentMarkItem::GetParam() const {
+RetainPtr<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 +38,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..f418aab 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;
@@ -24,13 +21,12 @@
 
   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:
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..95eaeea 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,12 +32,11 @@
   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:
@@ -53,12 +53,12 @@
 
     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:
     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..5ef9d80 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,61 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
+#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/pauseindicator_iface.h"
-#include "core/fxge/render_defines.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
 
 CPDF_ContentParser::CPDF_ContentParser(CPDF_Page* pPage)
-    : m_CurrentStage(Stage::kGetContent), m_pObjectHolder(pPage) {
-  ASSERT(pPage);
+    : m_CurrentStage(Stage::kGetContent), m_pPageObjectHolder(pPage) {
+  DCHECK(pPage);
   if (!pPage->GetDocument()) {
     m_CurrentStage = Stage::kComplete;
     return;
   }
 
-  CPDF_Object* pContent =
-      pPage->GetDict()->GetDirectObjectFor(pdfium::page_object::kContents);
+  RetainPtr<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,
+CPDF_ContentParser::CPDF_ContentParser(RetainPtr<const CPDF_Stream> pStream,
+                                       CPDF_PageObjectHolder* pPageObjectHolder,
                                        const CPDF_AllStates* pGraphicStates,
                                        const CFX_Matrix* pParentMatrix,
                                        CPDF_Type3Char* pType3Char,
                                        std::set<const uint8_t*>* pParsedSet)
     : 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 +88,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,
+      pParsedSet);
   m_pParser->GetCurStates()->m_CTM = form_matrix;
   m_pParser->GetCurStates()->m_ParentMatrix = form_matrix;
   if (ClipPath.HasRef()) {
-    m_pParser->GetCurStates()->m_ClipPath.AppendPath(ClipPath, FXFILL_WINDING,
-                                                     true);
+    m_pParser->GetCurStates()->m_ClipPath.AppendPathWithAutoMerge(
+        ClipPath, CFX_FillRenderOptions::FillType::kWinding);
   }
-  if (pForm->GetTransparency().IsGroup()) {
+  if (m_pPageObjectHolder->GetTransparency().IsGroup()) {
     CPDF_GeneralState* pState = &m_pParser->GetCurStates()->m_GeneralState;
     pState->SetBlendType(BlendMode::kNormal);
     pState->SetStrokeAlpha(1.0f);
     pState->SetFillAlpha(1.0f);
     pState->SetSoftMask(nullptr);
   }
-  m_pSingleStream = pdfium::MakeRetain<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 +138,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 +163,56 @@
   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_ParsedSet.clear();
+    m_pParser = std::make_unique<CPDF_StreamContentParser>(
+        m_pPageObjectHolder->GetDocument(),
+        m_pPageObjectHolder->GetMutablePageResources(), nullptr, nullptr,
+        m_pPageObjectHolder, m_pPageObjectHolder->GetMutableResources(),
+        m_pPageObjectHolder->GetBBox(), nullptr, &m_ParsedSet);
     m_pParser->GetCurStates()->m_ColorState.SetDefault();
   }
-  if (m_CurrentOffset >= m_Size)
+  if (m_CurrentOffset >= GetData().size())
     return Stage::kCheckClip;
 
   if (m_StreamSegmentOffsets.empty())
     m_StreamSegmentOffsets.push_back(0);
 
   static constexpr uint32_t kParseStepLimit = 100;
-  m_CurrentOffset += m_pParser->Parse(m_pData.Get(), m_Size, m_CurrentOffset,
+  m_CurrentOffset += m_pParser->Parse(GetData(), m_CurrentOffset,
                                       kParseStepLimit, m_StreamSegmentOffsets);
   return Stage::kParse;
 }
@@ -209,7 +223,7 @@
                                            m_pParser->GetType3Data());
   }
 
-  for (auto& pObj : *m_pObjectHolder) {
+  for (auto& pObj : *m_pPageObjectHolder) {
     if (!pObj->m_ClipPath.HasRef())
       continue;
     if (pObj->m_ClipPath.GetPathCount() != 1)
@@ -230,14 +244,15 @@
   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 +263,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..090c624 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_streamcontentparser.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
-#include "core/fxcrt/maybe_owned.h"
+#include "core/fxcrt/fixed_try_alloc_zeroed_data_vector.h"
+#include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/abseil-cpp/absl/types/variant.h"
 
 class CPDF_AllStates;
 class CPDF_Array;
-class CPDF_Form;
 class CPDF_Page;
 class CPDF_PageObjectHolder;
 class CPDF_Stream;
@@ -29,7 +31,8 @@
 class 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,
@@ -56,26 +59,30 @@
   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;
+  std::set<const uint8_t*> m_ParsedSet;  // Only used when parsing pages.
 
-  // Only used when parsing pages.
-  std::unique_ptr<std::set<const uint8_t*>> m_pParsedSet;
-
-  // |m_pParser| has a reference to |m_pParsedSet|, so must be below and thus
-  // destroyed first.
+  // Must not outlive |m_pParsedSet|.
   std::unique_ptr<CPDF_StreamContentParser> m_pParser;
 };
 
diff --git a/core/fpdfapi/page/cpdf_devicecs.cpp b/core/fpdfapi/page/cpdf_devicecs.cpp
index e557516..5590c5b 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,8 +15,9 @@
 #include "core/fpdfapi/parser/cpdf_string.h"
 #include "core/fxcodec/fx_codec.h"
 #include "core/fxge/dib/cfx_cmyk_to_srgb.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/cxx17_backports.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -28,9 +27,9 @@
 
 }  // namespace
 
-CPDF_DeviceCS::CPDF_DeviceCS(int family) : CPDF_ColorSpace(nullptr, family) {
-  ASSERT(family == PDFCS_DEVICEGRAY || family == PDFCS_DEVICERGB ||
-         family == PDFCS_DEVICECMYK);
+CPDF_DeviceCS::CPDF_DeviceCS(Family family) : CPDF_ColorSpace(family) {
+  DCHECK(family == Family::kDeviceGray || family == Family::kDeviceRGB ||
+         family == Family::kDeviceCMYK);
   SetComponentsForStockCS(ComponentsForFamily(GetFamily()));
 }
 
@@ -41,27 +40,26 @@
                                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 +71,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..29db5a4 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,41 @@
 #include "core/fpdfapi/page/cpdf_docpagedata.h"
 #include "core/fpdfapi/page/cpdf_image.h"
 #include "core/fpdfapi/page/cpdf_imageobject.h"
+#include "core/fpdfapi/page/cpdf_indexedcs.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
+#include "core/fpdfapi/parser/cpdf_name.h"
+#include "core/fpdfapi/parser/cpdf_number.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcodec/basic/basicmodule.h"
-#include "core/fxcodec/fx_codec.h"
-#include "core/fxcodec/jbig2/jbig2module.h"
+#include "core/fxcodec/jbig2/jbig2_decoder.h"
 #include "core/fxcodec/jpeg/jpegmodule.h"
 #include "core/fxcodec/jpx/cjpx_decoder.h"
-#include "core/fxcodec/jpx/jpxmodule.h"
 #include "core/fxcodec/scanlinedecoder.h"
-#include "core/fxcrt/cfx_fixedbufgrow.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxge/calculate_pitch.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/cxx17_backports.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
-constexpr int kMaxImageDimension = 0x01FFFF;
+bool IsValidDimension(int value) {
+  constexpr int kMaxImageDimension = 0x01FFFF;
+  return value > 0 && value <= kMaxImageDimension;
+}
 
 unsigned int GetBits8(const uint8_t* pData, uint64_t bitpos, size_t nbits) {
-  ASSERT(nbits == 1 || nbits == 2 || nbits == 4 || nbits == 8 || nbits == 16);
-  ASSERT((bitpos & (nbits - 1)) == 0);
+  DCHECK(nbits == 1 || nbits == 2 || nbits == 4 || nbits == 8 || nbits == 16);
+  DCHECK_EQ((bitpos & (nbits - 1)), 0);
   unsigned int byte = pData[bitpos / 8];
   if (nbits == 8)
     return byte;
@@ -80,7 +90,7 @@
 }
 
 int CalculateBitsPerPixel(uint32_t bpc, uint32_t comps) {
-  // TODO(thestig): Can |bpp| be 0 here? Add an ASSERT() or handle it?
+  // TODO(thestig): Can |bpp| be 0 here? Add an DCHECK() or handle it?
   uint32_t bpp = bpc * comps;
   if (bpp == 1)
     return 1;
@@ -93,7 +103,7 @@
     CPDF_ColorSpace* pCS) {
   if (!pCS)
     return CJPX_Decoder::kNoColorSpace;
-  if (pCS->GetFamily() == PDFCS_INDEXED)
+  if (pCS->GetFamily() == CPDF_ColorSpace::Family::kIndexed)
     return CJPX_Decoder::kIndexedColorSpace;
   return CJPX_Decoder::kNormalColorSpace;
 }
@@ -101,179 +111,192 @@
 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;
 
     default:
-      return JpxDecodeAction::kDoNothing;
+      NOTREACHED_NORETURN();
+  }
+}
+
+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 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 +309,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 +331,43 @@
     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,
+    iDecodeStatus = Jbig2Decoder::StartDecode(
+        m_pJbig2Context.get(), m_pDocument->GetOrCreateCodecContext(), m_Width,
+        m_Height, pSrcSpan, nSrcKey, pGlobalSpan, nGlobalKey,
         m_pCachedBitmap->GetBuffer(), m_pCachedBitmap->GetPitch(), pPause);
   } else {
-    iDecodeStatus = pJbig2Module->ContinueDecode(m_pJbig2Context.get(), pPause);
+    iDecodeStatus = Jbig2Decoder::ContinueDecode(m_pJbig2Context.get(), pPause);
   }
 
-  if (iDecodeStatus < 0) {
+  if (iDecodeStatus == FXCODEC_STATUS::kError) {
     m_pJbig2Context.reset();
     m_pCachedBitmap.Reset();
     m_pGlobalAcc.Reset();
     return LoadState::kFail;
   }
-  if (iDecodeStatus == FXCODEC_STATUS_DECODE_TOBECONTINUE)
+  if (iDecodeStatus == FXCODEC_STATUS::kDecodeToBeContinued)
     return LoadState::kContinue;
 
   LoadState iContinueStatus = LoadState::kSuccess;
@@ -368,58 +387,51 @@
 
 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 +440,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 +486,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 +499,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 +513,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 +530,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 +546,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 +583,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 +593,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 +621,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,22 +656,35 @@
 
   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
@@ -656,19 +693,25 @@
     DCHECK_NE(0, m_nComponents);
   } else {
     DCHECK_EQ(0, m_nComponents);
-    m_nComponents = image_info.components;
+    m_nComponents = GetComponentCountFromOpjColorSpace(image_info.colorspace);
+    if (m_nComponents == 0) {
+      return nullptr;
+    }
   }
 
   FXDIB_Format format;
-  if (image_info.components == 1) {
-    format = FXDIB_8bppRgb;
-  } else if (image_info.components <= 3) {
-    format = FXDIB_Rgb;
-  } else if (image_info.components == 4) {
-    format = FXDIB_Rgb32;
+  if (action == JpxDecodeAction::kUseGray) {
+    format = FXDIB_Format::k8bppRgb;
+  } else if (action == JpxDecodeAction::kUseRgb && image_info.channels == 3) {
+    format = FXDIB_Format::kRgb;
+  } else if (action == JpxDecodeAction::kConvertArgbToRgb &&
+             image_info.channels == 4) {
+    format = FXDIB_Format::kRgb32;
+  } else if (action == JpxDecodeAction::kUseRgb && image_info.channels == 4) {
+    format = FXDIB_Format::kRgb32;
   } else {
-    image_info.width = (image_info.width * image_info.components + 2) / 3;
-    format = FXDIB_Rgb;
+    image_info.width = (image_info.width * image_info.channels + 2) / 3;
+    format = FXDIB_Format::kRgb;
   }
 
   auto result_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
@@ -677,44 +720,143 @@
 
   result_bitmap->Clear(0xFFFFFFFF);
   if (!decoder->Decode(result_bitmap->GetBuffer(), result_bitmap->GetPitch(),
-                       swap_rgb)) {
+                       swap_rgb, m_nComponents)) {
     return nullptr;
   }
 
-  if (m_pColorSpace && m_pColorSpace->GetFamily() == PDFCS_INDEXED &&
-      m_bpc < 8) {
+  if (convert_argb_to_rgb) {
+    DCHECK_EQ(3, m_nComponents);
+    auto rgb_bitmap = pdfium::MakeRetain<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 +890,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 +906,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 +921,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 +939,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 +962,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 +981,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 +1046,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,8 +1058,8 @@
       R = (1.0f - color_values[0]) * k;
       G = (1.0f - color_values[1]) * k;
       B = (1.0f - color_values[2]) * k;
-    } else if (m_Family != PDFCS_PATTERN) {
-      m_pColorSpace->GetRGB(color_values.data(), &R, &G, &B);
+    } else if (m_Family != CPDF_ColorSpace::Family::kPattern) {
+      m_pColorSpace->GetRGB(color_values, &R, &G, &B);
     }
     R = pdfium::clamp(R, 0.0f, 1.0f);
     G = pdfium::clamp(G, 0.0f, 1.0f);
@@ -926,12 +1072,13 @@
 }
 
 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 +1092,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 +1116,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 +1135,175 @@
   return true;
 }
 
-uint8_t* CPDF_DIB::GetBuffer() const {
-  return m_pCachedBitmap ? m_pCachedBitmap->GetBuffer() : nullptr;
+pdfium::span<uint8_t> CPDF_DIB::GetBuffer() const {
+  return m_pCachedBitmap ? m_pCachedBitmap->GetBuffer()
+                         : pdfium::span<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..a24d192 100644
--- a/core/fpdfapi/page/cpdf_dib.h
+++ b/core/fpdfapi/page/cpdf_dib.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,13 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_DIB_H_
 #define CORE_FPDFAPI_PAGE_CPDF_DIB_H_
 
+#include <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"
@@ -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<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..08b12fd 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/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..90a8650 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,7 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check_op.h"
 
 // static
 CPDF_Dictionary* CPDF_Form::ChooseResourcesDict(
@@ -28,22 +29,27 @@
 }
 
 CPDF_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();
 }
 
@@ -71,16 +77,11 @@
     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,
+        pParsedSet ? pParsedSet : &m_ParsedSet));
   }
-
-  ASSERT(GetParseState() == ParseState::kParsing);
+  DCHECK_EQ(GetParseState(), ParseState::kParsing);
   ContinueParse(nullptr);
 }
 
@@ -106,18 +107,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..9052b9d 100644
--- a/core/fpdfapi/page/cpdf_form.h
+++ b/core/fpdfapi/page/cpdf_form.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,18 +7,17 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_FORM_H_
 #define CORE_FPDFAPI_PAGE_CPDF_FORM_H_
 
-#include <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;
 
@@ -31,11 +30,11 @@
                                               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,7 +42,7 @@
   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();
@@ -51,7 +50,7 @@
                     const CFX_Matrix* pParentMatrix,
                     std::set<const uint8_t*>* pParsedSet);
 
-  const CPDF_Stream* GetStream() const;
+  RetainPtr<const CPDF_Stream> GetStream() const;
 
  private:
   void ParseContentInternal(const CPDF_AllStates* pGraphicStates,
@@ -59,7 +58,7 @@
                             CPDF_Type3Char* pType3Char,
                             std::set<const uint8_t*>* pParsedSet);
 
-  std::unique_ptr<std::set<const uint8_t*>> m_ParsedSet;
+  std::set<const uint8_t*> m_ParsedSet;
   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..85b9e41 100644
--- a/core/fpdfapi/page/cpdf_function.cpp
+++ b/core/fpdfapi/page/cpdf_function.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,7 @@
 
 #include "core/fpdfapi/page/cpdf_function.h"
 
+#include <utility>
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_expintfunc.h"
@@ -17,26 +18,45 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/scoped_set_insertion.h"
+#include "core/fxcrt/stl_util.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/cxx17_backports.h"
+
+namespace {
+
+CPDF_Function::Type IntegerToFunctionType(int iType) {
+  switch (iType) {
+    case 0:
+    case 2:
+    case 3:
+    case 4:
+      return static_cast<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] = pdfium::clamp(inputs[i], domain1, domain2);
   }
-  if (!v_Call(clamped_inputs.data(), results))
-    return false;
+  if (!v_Call(clamped_inputs, results))
+    return absl::nullopt;
 
   if (m_Ranges.empty())
-    return true;
+    return m_nOutputs;
 
   for (uint32_t i = 0; i < m_nOutputs; i++) {
-    results[i] =
-        pdfium::clamp(results[i], m_Ranges[i * 2], m_Ranges[i * 2 + 1]);
+    float range1 = m_Ranges[i * 2];
+    float range2 = m_Ranges[i * 2 + 1];
+    if (range1 > range2)
+      return absl::nullopt;
+
+    results[i] = pdfium::clamp(results[i], range1, range2);
   }
-  return true;
+  return m_nOutputs;
 }
 
 // See PDF Reference 1.7, page 170.
@@ -158,6 +170,7 @@
   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..faddb4e 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/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..0e040aa 100644
--- a/core/fpdfapi/page/cpdf_iccprofile.h
+++ b/core/fpdfapi/page/cpdf_iccprofile.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,8 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_ICCPROFILE_H_
 #define CORE_FPDFAPI_PAGE_CPDF_ICCPROFILE_H_
 
+#include <stdint.h>
+
 #include <memory>
 
 #include "core/fxcrt/observed_ptr.h"
@@ -16,29 +18,35 @@
 class CPDF_Stream;
 
 namespace fxcodec {
-class CLcmsCmm;
+class IccTransform;
 }  // namespace fxcodec
 
 class CPDF_IccProfile final : public Retainable, public Observable {
  public:
-  template <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..c9be231 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<CPDF_Dictionary> pStreamDict = m_pStream->GetMutableDict();
+  m_pOC = pStreamDict->GetMutableDictFor("OC");
   m_bIsMask = !pStreamDict->KeyExist("ColorSpace") ||
-              pStreamDict->GetIntegerFor("ImageMask");
+              pStreamDict->GetBooleanFor("ImageMask", /*bDefault=*/false);
   m_bInterpolate = !!pStreamDict->GetIntegerFor("Interpolate");
   m_Height = pStreamDict->GetIntegerFor("Height");
   m_Width = pStreamDict->GetIntegerFor("Width");
@@ -77,18 +82,26 @@
   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..04355a9 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"
 
 class CFX_DIBBase;
 class CFX_DIBitmap;
+class CPDF_DIB;
 class CPDF_Dictionary;
 class CPDF_Document;
 class CPDF_Page;
@@ -25,40 +26,41 @@
 
 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;
+  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 +68,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..c031502 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,29 @@
 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)
+  auto* pDoc = m_pImage->GetDocument();
+  if (!pDoc)
     return;
 
-  CPDF_Stream* pStream = m_pImage->GetStream();
+  RetainPtr<const CPDF_Stream> pStream = m_pImage->GetStream();
   if (!pStream)
     return;
 
@@ -88,5 +95,5 @@
     return;
 
   m_pImage.Reset();  // Clear my reference before asking the cache.
-  pPageData->MaybePurgeImage(objnum);
+  pDoc->MaybePurgeImage(objnum);
 }
diff --git a/core/fpdfapi/page/cpdf_imageobject.h b/core/fpdfapi/page/cpdf_imageobject.h
index 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..85f694f
--- /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/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..95f560f 100644
--- a/core/fpdfapi/page/cpdf_meshstream.cpp
+++ b/core/fpdfapi/page/cpdf_meshstream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,13 +6,16 @@
 
 #include "core/fpdfapi/page/cpdf_meshstream.h"
 
+#include <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 "core/fxcrt/cfx_bitstream.h"
+#include "third_party/base/check.h"
 #include "third_party/base/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..497cd8b
--- /dev/null
+++ b/core/fpdfapi/page/cpdf_pageimagecache.cpp
@@ -0,0 +1,262 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/page/cpdf_pageimagecache.h"
+
+#include <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/stl_util.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+
+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; }
+};
+
+}  // 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 = m_pCurBitmap->Realize();
+    m_pCurBitmap.Reset();
+  } else {
+    m_pCachedBitmap = m_pCurBitmap;
+  }
+  if (m_pCurMask) {
+    m_pCachedMask = m_pCurMask->Realize();
+    m_pCurMask.Reset();
+  }
+  m_pCurBitmap = m_pCachedBitmap;
+  m_pCurMask = m_pCachedMask;
+  CalcSize();
+}
+
+void CPDF_PageImageCache::Entry::CalcSize() {
+  m_dwCacheSize = 0;
+  if (m_pCachedBitmap)
+    m_dwCacheSize += m_pCachedBitmap->GetEstimatedImageMemoryBurden();
+  if (m_pCachedMask)
+    m_dwCacheSize += m_pCachedMask->GetEstimatedImageMemoryBurden();
+}
+
+bool CPDF_PageImageCache::Entry::IsCacheValid(
+    const CFX_Size& max_size_required) const {
+  if (!m_bCachedSetMaxSizeRequired) {
+    return true;
+  }
+  if (max_size_required.width == 0 && max_size_required.height == 0) {
+    return false;
+  }
+
+  return (m_pCachedBitmap->GetWidth() >= max_size_required.width) &&
+         (m_pCachedBitmap->GetHeight() >= max_size_required.height);
+}
diff --git a/core/fpdfapi/page/cpdf_pageimagecache.h b/core/fpdfapi/page/cpdf_pageimagecache.h
new file mode 100644
index 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..8636af6 100644
--- a/core/fpdfapi/page/cpdf_pageobject.cpp
+++ b/core/fpdfapi/page/cpdf_pageobject.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include "core/fpdfapi/page/cpdf_pageobject.h"
 
+#include "core/fxcrt/fx_coordinates.h"
+
 CPDF_PageObject::CPDF_PageObject(int32_t content_stream)
     : m_ContentStream(content_stream) {}
 
diff --git a/core/fpdfapi/page/cpdf_pageobject.h b/core/fpdfapi/page/cpdf_pageobject.h
index f840214..7d9d015 100644
--- a/core/fpdfapi/page/cpdf_pageobject.h
+++ b/core/fpdfapi/page/cpdf_pageobject.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,10 +7,12 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_
 
+#include <stdint.h>
+
 #include "core/fpdfapi/page/cpdf_contentmarks.h"
 #include "core/fpdfapi/page/cpdf_graphicstates.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 
 class CPDF_FormObject;
 class CPDF_ImageObject;
@@ -18,14 +20,18 @@
 class CPDF_ShadingObject;
 class CPDF_TextObject;
 
+// Represents an object within the page, like a form or image. Not to be
+// confused with the PDF spec's page object that lives in a page tree, which is
+// represented by CPDF_Page.
 class CPDF_PageObject : public CPDF_GraphicStates {
  public:
-  enum Type {
-    TEXT = 1,
-    PATH,
-    IMAGE,
-    SHADING,
-    FORM,
+  // Values must match corresponding values in //public.
+  enum class Type {
+    kText = 1,
+    kPath,
+    kImage,
+    kShading,
+    kForm,
   };
 
   static constexpr int32_t kNoContentStream = -1;
@@ -58,11 +64,19 @@
   void TransformClipPath(const CFX_Matrix& matrix);
   void TransformGeneralState(const CFX_Matrix& matrix);
 
+  void SetOriginalRect(const CFX_FloatRect& rect) { m_OriginalRect = rect; }
+  const CFX_FloatRect& GetOriginalRect() const { return m_OriginalRect; }
   void SetRect(const CFX_FloatRect& rect) { m_Rect = rect; }
   const CFX_FloatRect& GetRect() const { return m_Rect; }
   FX_RECT GetBBox() const;
   FX_RECT GetTransformedBBox(const CFX_Matrix& matrix) const;
 
+  CPDF_ContentMarks* GetContentMarks() { return &m_ContentMarks; }
+  const CPDF_ContentMarks* GetContentMarks() const { return &m_ContentMarks; }
+  void SetContentMarks(const CPDF_ContentMarks& marks) {
+    m_ContentMarks = marks;
+  }
+
   // Get what content stream the object was parsed from in its page. This number
   // is the index of the content stream in the "Contents" array, or 0 if there
   // is a single content stream. If the object is newly created,
@@ -75,16 +89,29 @@
     m_ContentStream = new_content_stream;
   }
 
-  CPDF_ContentMarks m_ContentMarks;
+  const ByteString& GetResourceName() const { return m_ResourceName; }
+  void SetResourceName(const ByteString& resource_name) {
+    m_ResourceName = resource_name;
+  }
+
+  const ByteString& GetGraphicsResourceName() const {
+    return m_GraphicsResourceName;
+  }
+  void SetGraphicsResourceName(const ByteString& resource_name) {
+    m_GraphicsResourceName = resource_name;
+  }
 
  protected:
   void CopyData(const CPDF_PageObject* pSrcObject);
 
-  CFX_FloatRect m_Rect;
-
  private:
+  CFX_FloatRect m_Rect;
+  CFX_FloatRect m_OriginalRect;
+  CPDF_ContentMarks m_ContentMarks;
   bool m_bDirty = false;
   int32_t m_ContentStream;
+  ByteString m_ResourceName;          // The resource name for this object.
+  ByteString m_GraphicsResourceName;  // Like `m_ResourceName` but for graphics.
 };
 
 #endif  // CORE_FPDFAPI_PAGE_CPDF_PAGEOBJECT_H_
diff --git a/core/fpdfapi/page/cpdf_pageobjectholder.cpp b/core/fpdfapi/page/cpdf_pageobjectholder.cpp
index 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..cd535b1 100644
--- a/core/fpdfapi/page/cpdf_pattern.h
+++ b/core/fpdfapi/page/cpdf_pattern.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,13 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PATTERN_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PATTERN_H_
 
+#include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fxcrt/fx_coordinates.h"
-#include "core/fxcrt/fx_system.h"
 #include "core/fxcrt/observed_ptr.h"
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/unowned_ptr.h"
 
 class CPDF_Document;
-class CPDF_Object;
 class CPDF_ShadingPattern;
 class CPDF_TilingPattern;
 
@@ -28,17 +27,18 @@
   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);
 
+  // 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();
 
  private:
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..d7f6f7e 100644
--- a/core/fpdfapi/page/cpdf_psengine.h
+++ b/core/fpdfapi/page/cpdf_psengine.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,11 +7,13 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_PSENGINE_H_
 #define CORE_FPDFAPI_PAGE_CPDF_PSENGINE_H_
 
+#include <stddef.h>
+#include <stdint.h>
+
 #include <memory>
 #include <vector>
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/bytestring.h"
 #include "third_party/base/span.h"
 
 class CPDF_PSEngine;
@@ -72,8 +74,9 @@
   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..0aa28f4 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 <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"
+#include "third_party/base/cxx17_backports.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)
@@ -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..0522cf1 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.cpp
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -34,17 +34,22 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxcrt/autonuller.h"
+#include "core/fxcrt/bytestring.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/scoped_set_insertion.h"
+#include "core/fxcrt/stl_util.h"
+#include "core/fxge/cfx_graphstate.h"
 #include "core/fxge/cfx_graphstatedata.h"
-#include "core/fxge/render_defines.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/no_destructor.h"
+#include "third_party/base/notreached.h"
 #include "third_party/base/span.h"
-#include "third_party/base/stl_util.h"
 
 namespace {
 
-const int kMaxFormLevel = 30;
+const int kMaxFormLevel = 40;
 
 const int kSingleCoordinatePair = 1;
 const int kTensorCoordinatePairs = 16;
@@ -60,47 +65,39 @@
 const char 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 +110,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 +128,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 +161,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 +178,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 +188,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 +200,7 @@
           replacements.push_back(op);
         }
       } else {
-        ReplaceAbbr(value);
+        ReplaceAbbr(std::move(value));
       }
     }
   }
@@ -219,28 +214,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 +243,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)
     : m_pDocument(pDocument),
       m_pPageResources(pPageResources),
       m_pParentResources(pParentResources),
-      m_pResources(CPDF_Form::ChooseResourcesDict(pResources,
-                                                  pParentResources,
-                                                  pPageResources)),
+      m_pResources(CPDF_Form::ChooseResourcesDict(pResources.Get(),
+                                                  pParentResources.Get(),
+                                                  pPageResources.Get())),
       m_pObjectHolder(pObjHolder),
       m_ParsedSet(pParsedSet),
       m_BBox(rcBBox),
-      m_pCurStates(pdfium::MakeUnique<CPDF_AllStates>()) {
+      m_pCurStates(std::make_unique<CPDF_AllStates>()) {
   if (pmtContentToUser)
     m_mtContentToUser = *pmtContentToUser;
   if (pStates) {
@@ -279,7 +273,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 +286,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 +301,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 +330,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 +339,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 +367,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 +385,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 +401,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 +426,7 @@
   if (bText) {
     pObj->m_TextState = m_pCurStates->m_TextState;
   }
+  pObj->SetGraphicsResourceName(m_pCurStates->m_GraphicsResourceName);
 }
 
 // static
@@ -549,33 +551,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 +588,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 +604,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 +639,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 +674,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 +689,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 +698,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 +727,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 +735,24 @@
     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);
+    AddForm(std::move(pXObject), name);
     return;
   }
 
   if (type == "Image") {
-    CPDF_ImageObject* pObj = pXObject->IsInline()
-                                 ? AddImage(ToStream(pXObject->Clone()))
-                                 : AddImage(pXObject->GetObjNum());
+    CPDF_ImageObject* pObj =
+        pXObject->IsInline()
+            ? AddImageFromStream(ToStream(pXObject->Clone()), name)
+            : AddImageFromStreamObjNum(pXObject->GetObjNum(), name);
 
     m_LastImageName = std::move(name);
     if (pObj) {
@@ -763,20 +763,22 @@
   }
 }
 
-void CPDF_StreamContentParser::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_ParsedSet);
 
   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->SetGraphicsResourceName(m_pCurStates->m_GraphicsResourceName);
   if (!m_pObjectHolder->BackgroundAlphaNeeded() &&
       pFormObj->form()->BackgroundAlphaNeeded()) {
     m_pObjectHolder->SetBackgroundAlphaNeeded(true);
@@ -786,37 +788,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 +830,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 +838,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 +873,49 @@
 }
 
 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);
+
+  m_pCurStates->m_GraphicsResourceName = name;
+  m_pCurStates->ProcessExtGS(pGS.Get(), this);
 }
 
 void CPDF_StreamContentParser::Handle_ClosePath() {
   if (m_PathPoints.empty())
     return;
 
-  if (m_PathStartX != m_PathCurrentX || m_PathStartY != m_PathCurrentY)
-    AddPathPoint(m_PathStartX, m_PathStartY, FXPT_TYPE::LineTo, true);
-  else if (m_PathPoints.back().m_Type != FXPT_TYPE::MoveTo)
+  if (m_PathStart.x != m_PathCurrent.x || m_PathStart.y != m_PathCurrent.y) {
+    AddPathPointAndClose(m_PathStart, CFX_Path::Point::Type::kLine);
+  } else {
     m_PathPoints.back().m_CloseFigure = true;
+  }
 }
 
 void CPDF_StreamContentParser::Handle_SetFlat() {
@@ -934,32 +938,32 @@
   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 +974,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 +992,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 +1047,7 @@
 }
 
 void CPDF_StreamContentParser::Handle_SetColorPS_Fill() {
-  CPDF_Object* pLastParam = GetObject(0);
+  RetainPtr<CPDF_Object> pLastParam = GetObject(0);
   if (!pLastParam)
     return;
 
@@ -1050,13 +1058,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 +1078,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 +1095,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 +1111,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 +1121,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 +1173,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 +1239,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 +1258,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 +1292,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 +1316,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 +1342,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 +1354,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 +1390,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 +1400,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 +1426,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;
+      pdfium::Contains(*m_ParsedSet, 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*> scopedInsert(m_ParsedSet,
+                                                  pDataStart.data());
 
   uint32_t init_obj_count = m_pObjectHolder->GetPageObjectCount();
-  CPDF_StreamParser syntax(pdfium::make_span(pDataStart, size_left),
-                           m_pDocument->GetByteStringPool());
-  CPDF_StreamParserAutoClearer auto_clearer(&m_pSyntax, &syntax);
-  while (1) {
+  AutoNuller<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 +1581,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 +1652,7 @@
         }
         break;
       }
-      case CPDF_StreamParser::Number: {
+      case CPDF_StreamParser::ElementType::kNumber: {
         if (nParams == 6)
           break;
 
@@ -1630,15 +1673,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..276dc72 100644
--- a/core/fpdfapi/page/cpdf_streamcontentparser.h
+++ b/core/fpdfapi/page/cpdf_streamcontentparser.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,10 +14,14 @@
 #include <vector>
 
 #include "core/fpdfapi/page/cpdf_contentmarks.h"
+#include "core/fxcrt/bytestring.h"
+#include "core/fxcrt/fx_coordinates.h"
 #include "core/fxcrt/fx_number.h"
-#include "core/fxcrt/fx_string.h"
 #include "core/fxcrt/retain_ptr.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxcrt/unowned_ptr.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_path.h"
+#include "third_party/base/span.h"
 
 class CPDF_AllStates;
 class CPDF_ColorSpace;
@@ -30,6 +34,7 @@
 class CPDF_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_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);
@@ -212,21 +227,18 @@
   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..1a0f115 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"
 
 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..a063541 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/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..932d5a7 100644
--- a/core/fpdfapi/page/cpdf_textstate.h
+++ b/core/fpdfapi/page/cpdf_textstate.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -10,6 +10,7 @@
 #include "core/fxcrt/retain_ptr.h"
 #include "core/fxcrt/shared_copy_on_write.h"
 #include "core/fxcrt/unowned_ptr.h"
+#include "third_party/base/span.h"
 
 class CPDF_Document;
 class CPDF_Font;
@@ -36,13 +37,13 @@
   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..d168930 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 "core/fxge/dib/fx_dib.h"
 #include "third_party/base/span.h"
 
-class CPDF_Document;
 class CFX_DIBBase;
 
 class CPDF_TransferFunc final : public Retainable, public Observable {
  public:
-  template <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..a034116 100644
--- a/core/fpdfapi/page/cpdf_transferfuncdib.h
+++ b/core/fpdfapi/page/cpdf_transferfuncdib.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,9 @@
 #ifndef CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNCDIB_H_
 #define CORE_FPDFAPI_PAGE_CPDF_TRANSFERFUNCDIB_H_
 
-#include <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"
@@ -17,39 +18,25 @@
 
 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..5e9b6d3 100644
--- a/core/fpdfapi/parser/cfdf_document.cpp
+++ b/core/fpdfapi/parser/cfdf_document.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,8 +13,8 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
+#include "core/fxcrt/fx_string_wrappers.h"
 #include "third_party/base/span.h"
 
 CFDF_Document::CFDF_Document() = default;
@@ -22,36 +22,35 @@
 CFDF_Document::~CFDF_Document() = default;
 
 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..bd0e72f 100644
--- a/core/fpdfapi/parser/cfdf_document.h
+++ b/core/fpdfapi/parser/cfdf_document.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,7 +26,8 @@
   ~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..7f0b7fe 100644
--- a/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
+++ b/core/fpdfapi/parser/cpdf_cross_ref_avail.cpp
@@ -1,19 +1,18 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_cross_ref_avail.h"
 
-#include <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/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
 
 namespace {
 
@@ -30,20 +29,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;
@@ -56,7 +55,7 @@
       case State::kDone:
         break;
       default: {
-        current_status_ = CPDF_DataAvail::DataError;
+        status_ = CPDF_DataAvail::kDataError;
         NOTREACHED();
         break;
       }
@@ -64,14 +63,14 @@
     if (!check_result)
       break;
 
-    ASSERT(!GetValidator()->has_read_problems());
+    DCHECK(!GetValidator()->has_read_problems());
   }
-  return current_status_;
+  return status_;
 }
 
 bool CPDF_CrossRefAvail::CheckReadProblems() {
   if (GetValidator()->read_error()) {
-    current_status_ = CPDF_DataAvail::DataError;
+    status_ = CPDF_DataAvail::kDataError;
     return true;
   }
   return GetValidator()->has_unavailable_data();
@@ -80,13 +79,13 @@
 bool CPDF_CrossRefAvail::CheckCrossRef() {
   if (cross_refs_for_check_.empty()) {
     // All cross refs were checked.
-    current_state_ = State::kDone;
-    current_status_ = CPDF_DataAvail::DataAvailable;
+    state_ = State::kDone;
+    status_ = CPDF_DataAvail::kDataAvailable;
     return true;
   }
   parser_->SetPos(cross_refs_for_check_.front());
 
-  const ByteString first_word = parser_->PeekNextWord(nullptr);
+  const ByteString first_word = parser_->PeekNextWord();
   if (CheckReadProblems())
     return false;
 
@@ -105,36 +104,36 @@
     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 +141,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 +173,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..0bb5bb6 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(
@@ -112,7 +118,7 @@
 
   objects_info_.erase(objects_info_.lower_bound(objnum), objects_info_.end());
 
-  if (!pdfium::ContainsKey(objects_info_, objnum - 1))
+  if (!pdfium::Contains(objects_info_, objnum - 1))
     objects_info_[objnum - 1].pos = 0;
 }
 
@@ -154,5 +160,5 @@
   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..246e129 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(); }
 
@@ -69,6 +80,10 @@
   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..5404207 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) {
@@ -278,19 +233,18 @@
 
   struct MayBeSignature {
     const CPDF_Dictionary* parent;
-    CPDF_Object* contents;
+    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,22 @@
         // Temporary skip it, to prevent signature corruption.
         // It will be decrypted on next interations, if this is not contents of
         // signature dictionary.
-        may_be_sign_dictionaries.push(MayBeSignature({parent_dict, child}));
+        may_be_sign_dictionaries.push({parent_dict.Get(), std::move(child)});
         walker.SkipWalkIntoCurrentObject();
         continue;
       }
       // Strings decryption.
       if (child->IsString()) {
         // TODO(art-snake): Move decryption into the CPDF_String class.
-        CPDF_String* str = child->AsString();
+        CPDF_String* str = child->AsMutableString();
         str->SetString(Decrypt(obj_num, gen_num, str->GetString()));
       }
       // Stream decryption.
       if (child->IsStream()) {
         // TODO(art-snake): Move decryption into the CPDF_Stream class.
-        CPDF_Stream* stream = child->AsStream();
-        auto stream_access = pdfium::MakeRetain<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 +278,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 +286,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 +307,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 +332,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..b8f938c 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 "core/fxcrt/retain_ptr.h"
 #include "third_party/base/span.h"
 
 class CPDF_Dictionary;
 class CPDF_Object;
-class CPDF_SecurityHandler;
 
 class CPDF_CryptoHandler {
  public:
-  CPDF_CryptoHandler(int cipher, const uint8_t* key, size_t keylen);
-  ~CPDF_CryptoHandler();
+  enum class Cipher {
+    kNone = 0,
+    kRC4 = 1,
+    kAES = 2,
+    kAES2 = 3,
+  };
 
   static bool IsSignatureDictionary(const CPDF_Dictionary* dictionary);
 
+  CPDF_CryptoHandler(Cipher cipher, const uint8_t* key, size_t keylen);
+  ~CPDF_CryptoHandler();
+
   bool DecryptObjectTree(RetainPtr<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..7db348a 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,27 +171,27 @@
 
 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;
+    case kDataError:
+      m_internalStatus = InternalStatus::kError;
       return false;
     default:
       NOTREACHED();
@@ -202,33 +200,29 @@
 
   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 +230,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 +309,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 +338,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,37 +389,37 @@
   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();
@@ -414,7 +431,7 @@
   if (!m_pLinearized->GetFirstPageEndOffset() ||
       !m_pLinearized->GetFileSize() ||
       !m_pLinearized->GetMainXRefTableFirstEntryOffset()) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return false;
   }
 
@@ -429,24 +446,23 @@
                                                              data_size))
     return false;
 
-  m_docStatus =
-      m_bSupportHintTable ? PDF_DATAAVAIL_HINTTABLE : PDF_DATAAVAIL_DONE;
+  m_internalStatus = InternalStatus::kHintTable;
   return true;
 }
 
 bool CPDF_DataAvail::CheckHintTables() {
-  const CPDF_ReadValidator::Session read_session(GetValidator());
+  CPDF_ReadValidator::ScopedSession read_session(GetValidator());
   m_pHintTables =
       CPDF_HintTables::Parse(GetSyntaxParser(), m_pLinearized.get());
 
   if (GetValidator()->read_error()) {
-    m_docStatus = PDF_DATAAVAIL_ERROR;
+    m_internalStatus = InternalStatus::kError;
     return true;
   }
   if (GetValidator()->has_unavailable_data())
     return false;
 
-  m_docStatus = PDF_DATAAVAIL_DONE;
+  m_internalStatus = InternalStatus::kDone;
   return true;
 }
 
@@ -466,59 +482,59 @@
 
 CPDF_DataAvail::DocLinearizationStatus CPDF_DataAvail::IsLinearizedPDF() {
   switch (CheckHeaderAndLinearized()) {
-    case DocAvailStatus::DataAvailable:
-      return m_pLinearized ? DocLinearizationStatus::Linearized
-                           : DocLinearizationStatus::NotLinearized;
-    case DocAvailStatus::DataNotAvailable:
-      return DocLinearizationStatus::LinearizationUnknown;
-    case DocAvailStatus::DataError:
-      return DocLinearizationStatus::NotLinearized;
+    case kDataAvailable:
+      return m_pLinearized ? kLinearized : kNotLinearized;
+    case kDataNotAvailable:
+      return kLinearizationUnknown;
+    case kDataError:
+      return kNotLinearized;
     default:
       NOTREACHED();
-      return DocLinearizationStatus::LinearizationUnknown;
+      return kLinearizationUnknown;
   }
 }
 
 CPDF_DataAvail::DocAvailStatus CPDF_DataAvail::CheckHeaderAndLinearized() {
   if (m_bHeaderAvail)
-    return DocAvailStatus::DataAvailable;
+    return kDataAvailable;
 
-  const CPDF_ReadValidator::Session read_session(GetValidator());
-  const Optional<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 +545,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 +576,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 +585,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 +649,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 +659,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 +693,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 +712,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 +734,7 @@
     return false;
 
   if (CheckPageCount()) {
-    m_docStatus = PDF_DATAAVAIL_PAGE;
+    m_internalStatus = InternalStatus::kPage;
     return true;
   }
 
@@ -740,11 +757,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 +769,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 +837,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 +934,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 +958,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 +973,67 @@
 
 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;
+    case kDataError:
+      return kFormError;
+    case kDataNotAvailable:
+      return kFormNotAvailable;
+    case kDataAvailable:
+      return kFormAvailable;
     default:
       NOTREACHED();
   }
-  return DocFormStatus::FormError;
+  return kFormError;
 }
 
 bool CPDF_DataAvail::ValidatePage(uint32_t dwPage) const {
-  FX_SAFE_INT32 safePage = pdfium::base::checked_cast<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 +1050,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..37f0763 100644
--- a/core/fpdfapi/parser/cpdf_encryptor.h
+++ b/core/fpdfapi/parser/cpdf_encryptor.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,8 +9,7 @@
 
 #include <stdint.h>
 
-#include <vector>
-
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/unowned_ptr.h"
 #include "third_party/base/span.h"
 
@@ -18,13 +17,13 @@
 
 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..2d26d9b 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/abseil-cpp/absl/types/variant.h"
 #include "third_party/base/span.h"
 
 class CPDF_Dictionary;
+class CPDF_Encryptor;
 class CPDF_Stream;
 class CPDF_StreamAcc;
+class IFX_ArchiveStream;
 
 class CPDF_FlateEncoder {
  public:
-  CPDF_FlateEncoder(const CPDF_Stream* pStream, bool bFlateEncode);
+  CPDF_FlateEncoder(RetainPtr<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..ba3173d 100644
--- a/core/fpdfapi/parser/cpdf_hint_tables.cpp
+++ b/core/fpdfapi/parser/cpdf_hint_tables.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,14 +13,15 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_linearized_header.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_read_validator.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fxcrt/cfx_bitstream.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/check.h"
 #include "third_party/base/numerics/safe_conversions.h"
-#include "third_party/base/ptr_util.h"
 #include "third_party/base/span.h"
 
 namespace {
@@ -45,8 +46,8 @@
 //  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..f4de7d7 100644
--- a/core/fpdfapi/parser/cpdf_linearized_header.cpp
+++ b/core/fpdfapi/parser/cpdf_linearized_header.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,8 +13,10 @@
 #include "core/fpdfapi/parser/cpdf_array.h"
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_number.h"
+#include "core/fpdfapi/parser/cpdf_parser.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/check.h"
 #include "third_party/base/ptr_util.h"
 
 namespace {
@@ -24,12 +26,12 @@
 
 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..1c5a7b7 100644
--- a/core/fpdfapi/parser/cpdf_object_stream.cpp
+++ b/core/fpdfapi/parser/cpdf_object_stream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,26 +13,26 @@
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fpdfapi/parser/fpdf_parser_utility.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
 #include "core/fxcrt/fx_safe_types.h"
+#include "third_party/base/check.h"
 #include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
 
-// static
-bool CPDF_ObjectStream::IsObjectsStreamObject(const CPDF_Object* object) {
+namespace {
+
+bool IsObjectStream(const CPDF_Object* object) {
   const CPDF_Stream* stream = ToStream(object);
   if (!stream)
     return false;
 
-  const CPDF_Dictionary* stream_dict = stream->GetDict();
-  if (!stream_dict)
+  // 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..302aec1 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,10 +28,15 @@
 #include "core/fpdfapi/parser/cpdf_syntax_parser.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcrt/autorestorer.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/scoped_set_insertion.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/span.h"
 
 namespace {
 
@@ -39,13 +47,99 @@
 // "%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 +151,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 +178,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;
 }
@@ -112,15 +204,15 @@
   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 +222,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 +295,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 +313,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 +330,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 +355,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 +381,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);
 
-  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 +442,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 +511,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 +531,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 +567,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 +581,7 @@
         info.type = ObjectType::kNotCompressed;
       }
     }
-    nBytesToRead -= block_size;
+    entries_to_read -= entries_in_block;
   }
   return true;
 }
@@ -502,14 +593,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,7 +645,8 @@
         m_CrossRefTable->AddNormal(obj.obj_num, obj.info.gennum, obj.info.pos);
         break;
       case ObjectType::kCompressed:
-        m_CrossRefTable->AddCompressed(obj.obj_num, obj.info.archive_obj_num);
+        m_CrossRefTable->AddCompressed(obj.obj_num, obj.info.archive.obj_num,
+                                       obj.info.archive.obj_index);
         break;
       default:
         NOTREACHED();
@@ -567,13 +659,13 @@
     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 +674,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 +699,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 +721,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 +759,153 @@
   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 = std::make_unique<CPDF_CrossRefTable>(
+        std::move(pNewTrailer), pStream->GetObjNum());
     m_CrossRefTable->ShrinkObjectMap(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) {
-      continue;
-    }
-
-    const uint8_t* segstart = pData + segindex * totalWidth;
-    FX_SAFE_UINT32 dwMaxObjNum = startnum;
-    dwMaxObjNum += count;
+    pdfium::span<const uint8_t> seg_span = data_span.subspan(
+        segindex * total_width, index.obj_count * total_width);
+    FX_SAFE_UINT32 dwMaxObjNum = index.start_obj_num;
+    dwMaxObjNum += index.obj_count;
     uint32_t dwV5Size =
         m_CrossRefTable->objects_info().empty() ? 0 : GetLastObjNum() + 1;
     if (!dwMaxObjNum.IsValid() || dwMaxObjNum.ValueOrDie() > dwV5Size)
       continue;
 
-    for (uint32_t i = 0; i < count; i++) {
-      ObjectType type = ObjectType::kNotCompressed;
-      const uint8_t* entrystart = segstart + i * totalWidth;
-      if (WidthArray[0]) {
-        const uint32_t cross_ref_stream_obj_type =
-            GetVarInt(entrystart, WidthArray[0]);
-        type = GetObjectTypeFromCrossRefStreamType(cross_ref_stream_obj_type);
-        if (type == ObjectType::kNull)
-          continue;
-      }
+    for (uint32_t i = 0; i < index.obj_count; ++i) {
+      const uint32_t obj_num = index.start_obj_num + i;
+      if (obj_num >= CPDF_Parser::kMaxObjectNumber)
+        break;
 
-      const uint32_t objnum = startnum + i;
-      if (objnum >= CPDF_Parser::kMaxObjectNumber)
-        continue;
-
-      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);
+      ProcessCrossRefV5Entry(seg_span.subspan(i * total_width, total_width),
+                             field_widths, obj_num);
     }
-    segindex += count;
+
+    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 = ObjectType::kNotCompressed;
+  if (field_widths[0]) {
+    const uint32_t cross_ref_stream_obj_type =
+        GetFirstXRefStreamEntry(entry_span, field_widths);
+    type = GetObjectTypeFromCrossRefStreamType(cross_ref_stream_obj_type);
+    if (type == ObjectType::kNull)
+      return;
+  }
+
+  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 +926,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 +937,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 +945,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 +957,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 +970,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 +996,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 +1018,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 +1033,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 +1050,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 +1062,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 +1094,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 +1138,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 +1158,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 +1188,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..21dd8aa 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,7 +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.
@@ -168,13 +173,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 +186,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 +200,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..efde90e 100644
--- a/core/fpdfapi/parser/cpdf_parser_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_parser_unittest.cpp
@@ -1,23 +1,28 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "core/fpdfapi/parser/cpdf_parser.h"
+
 #include <limits>
 #include <memory>
 #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::Return;
 
 namespace {
 
@@ -27,13 +32,23 @@
   return info ? *info : CPDF_CrossRefTable::ObjectInfo();
 }
 
+class TestObjectsHolder final : public CPDF_Parser::ParsedObjectsHolder {
+ public:
+  TestObjectsHolder() = default;
+  ~TestObjectsHolder() override = default;
+
+  // CPDF_Parser::ParsedObjectsHolder:
+  bool TryInit() override { return true; }
+  MOCK_METHOD1(ParseIndirectObject, RetainPtr<CPDF_Object>(uint32_t objnum));
+};
+
 }  // namespace
 
 // A wrapper class to help test member functions of CPDF_Parser.
 class CPDF_TestParser final : public CPDF_Parser {
  public:
-  CPDF_TestParser() {}
-  ~CPDF_TestParser() {}
+  CPDF_TestParser() : CPDF_Parser(&object_holder_) {}
+  ~CPDF_TestParser() = default;
 
   // Setup reading from a file and initial states.
   bool InitTestFromFile(const char* path) {
@@ -43,15 +58,16 @@
       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 +75,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 +98,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 +118,7 @@
   ASSERT_FALSE(parser.RebuildCrossRef());
 }
 
-TEST(cpdf_parser, LoadCrossRefV4) {
+TEST(ParserTest, LoadCrossRefV4) {
   {
     static const unsigned char kXrefTable[] =
         "xref \n"
@@ -119,9 +142,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 +182,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 +222,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 +254,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 +287,7 @@
   }
 }
 
-TEST(cpdf_parser, ParseStartXRef) {
+TEST(ParserTest, ParseStartXRef) {
   CPDF_TestParser parser;
   std::string test_file;
   ASSERT_TRUE(
@@ -278,7 +301,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 +311,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 +323,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 +332,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 +360,103 @@
   ASSERT_TRUE(parser.GetCrossRefTable());
   EXPECT_EQ(0u, parser.GetCrossRefTable()->objects_info().size());
 }
+
+TEST(ParserTest, XrefObjectIndicesTooBig) {
+  CPDF_TestParser parser;
+
+  // Satisfy CPDF_Parser's checks, so the test data below can concentrate on the
+  // /XRef stream and avoid also providing other valid dictionaries.
+  auto dummy_root = pdfium::MakeRetain<CPDF_Dictionary>();
+  EXPECT_CALL(parser.object_holder(), ParseIndirectObject)
+      .WillRepeatedly(Return(dummy_root));
+
+  // Since /Index starts at 4194303, the object number will go past
+  // `kMaxObjectNumber`.
+  static_assert(CPDF_Parser::kMaxObjectNumber == 4194304,
+                "Unexpected kMaxObjectNumber");
+  const unsigned char kData[] =
+      "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Index [4194303 3]\n"
+      "  /Root 1 0 R\n"
+      "  /Size 4194306\n"
+      "  /W [1 1 1]\n"
+      ">>\n"
+      "stream\n"
+      "01 00 00\n"
+      "01 0F 00\n"
+      "01 12 00\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+  ASSERT_TRUE(parser.InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::SUCCESS, parser.StartParseInternal());
+  ASSERT_TRUE(parser.GetCrossRefTable());
+  const auto& objects_info = parser.GetCrossRefTable()->objects_info();
+  EXPECT_EQ(2u, objects_info.size());
+
+  // This should be the only object from table. Subsequent objects have object
+  // numbers that are too big.
+  auto first_object_it = objects_info.find(4194303);
+  ASSERT_NE(first_object_it, objects_info.end());
+  EXPECT_EQ(CPDF_Parser::ObjectType::kNormal, first_object_it->second.type);
+  EXPECT_EQ(0, first_object_it->second.pos);
+
+  // TODO(thestig): Should the xref table contain object 4194305?
+  // Consider reworking CPDF_Parser's object representation to avoid having to
+  // store this placeholder object.
+  auto placeholder_object_it = objects_info.find(4194305);
+  ASSERT_NE(placeholder_object_it, objects_info.end());
+  EXPECT_EQ(CPDF_Parser::ObjectType::kFree, placeholder_object_it->second.type);
+}
+
+TEST(ParserTest, XrefHasInvalidArchiveObjectNumber) {
+  CPDF_TestParser parser;
+
+  // Satisfy CPDF_Parser's checks, so the test data below can concentrate on the
+  // /XRef stream and avoid also providing other valid dictionaries.
+  auto dummy_root = pdfium::MakeRetain<CPDF_Dictionary>();
+  EXPECT_CALL(parser.object_holder(), ParseIndirectObject)
+      .WillRepeatedly(Return(dummy_root));
+
+  // 0xFF in the first object in the xref object stream is invalid.
+  const unsigned char kData[] =
+      "%PDF1-7\n%\xa0\xf2\xa4\xf4\n"
+      "7 0 obj <<\n"
+      "  /Filter /ASCIIHexDecode\n"
+      "  /Root 1 0 R\n"
+      "  /Size 3\n"
+      "  /W [1 1 1]\n"
+      ">>\n"
+      "stream\n"
+      "02 FF 00\n"
+      "01 0F 00\n"
+      "01 12 00\n"
+      "endstream\n"
+      "endobj\n"
+      "startxref\n"
+      "14\n"
+      "%%EOF\n";
+  ASSERT_TRUE(parser.InitTestFromBuffer(kData));
+  EXPECT_EQ(CPDF_Parser::SUCCESS, parser.StartParseInternal());
+
+  const CPDF_CrossRefTable* cross_ref_table = parser.GetCrossRefTable();
+  ASSERT_TRUE(cross_ref_table);
+  EXPECT_EQ(7u, cross_ref_table->trailer_object_number());
+  const auto& objects_info = cross_ref_table->objects_info();
+  EXPECT_EQ(2u, objects_info.size());
+
+  // Skip over the first object, and continue parsing the remaining objects.
+  auto second_object_it = objects_info.find(1);
+  ASSERT_NE(second_object_it, objects_info.end());
+  EXPECT_EQ(CPDF_Parser::ObjectType::kNormal, second_object_it->second.type);
+  EXPECT_EQ(15, second_object_it->second.pos);
+
+  auto third_object_it = objects_info.find(2);
+  ASSERT_NE(third_object_it, objects_info.end());
+  EXPECT_EQ(CPDF_Parser::ObjectType::kNormal, third_object_it->second.type);
+  EXPECT_EQ(18, third_object_it->second.pos);
+}
diff --git a/core/fpdfapi/parser/cpdf_read_validator.cpp b/core/fpdfapi/parser/cpdf_read_validator.cpp
index 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..ff17c59 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,11 @@
 #include "core/fpdfapi/parser/cpdf_dictionary.h"
 #include "core/fpdfapi/parser/cpdf_object.h"
 #include "core/fpdfapi/parser/cpdf_string.h"
+#include "core/fxcrt/data_vector.h"
 #include "core/fxcrt/fx_random.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/notreached.h"
 
 namespace {
 
@@ -47,7 +50,7 @@
   GetPassCode(password, passcode);
   CRYPT_md5_context md5 = CRYPT_MD5Start();
   CRYPT_MD5Update(&md5, passcode);
-  ByteString okey = pEncrypt->GetStringFor("O");
+  ByteString okey = pEncrypt->GetByteStringFor("O");
   CRYPT_MD5Update(&md5, okey.raw_span());
   uint32_t perm = pEncrypt->GetIntegerFor("P");
   CRYPT_MD5Update(&md5, pdfium::as_bytes(pdfium::make_span(&perm, 1)));
@@ -70,20 +73,18 @@
   memcpy(key, digest, copy_len);
 }
 
-bool IsValidKeyLengthForCipher(int cipher, size_t keylen) {
+bool IsValidKeyLengthForCipher(CPDF_CryptoHandler::Cipher cipher,
+                               size_t keylen) {
   switch (cipher) {
-    case FXCIPHER_AES:
+    case CPDF_CryptoHandler::Cipher::kAES:
       return keylen == 16 || keylen == 24 || keylen == 32;
-    case FXCIPHER_AES2:
+    case CPDF_CryptoHandler::Cipher::kAES2:
       return keylen == 32;
-    case FXCIPHER_RC4:
+    case CPDF_CryptoHandler::Cipher::kRC4:
       return keylen >= 5 && keylen <= 16;
-    case FXCIPHER_NONE:
+    case CPDF_CryptoHandler::Cipher::kNone:
       return true;
-    default:
-      NOTREACHED();
   }
-  return false;
 }
 
 #define FX_GET_32WORD(n, b, i)                                        \
@@ -118,13 +119,13 @@
   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 +137,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 +146,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 +189,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 +216,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 +227,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 +263,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 +288,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 +297,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 +307,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 +322,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 +361,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 +385,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 +441,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 +474,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 +543,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 +556,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 +594,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 +650,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 +681,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 +710,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 +722,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..f2c5ac1 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,16 @@
   EXPECT_EQ(0xFFFFFFFC, FPDF_GetDocPermissions(document()));
 }
 
-// TODO(crbug.com/pdfium/11): Fix this test and enable.
-#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
-#define MAYBE_PasswordAfterGenerateSave DISABLED_PasswordAfterGenerateSave
+TEST_F(CPDFSecurityHandlerEmbedderTest, PasswordAfterGenerateSave) {
+  const char* checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "df9fe67555b7ceb59c99036e8d2c1c76";
+#if BUILDFLAG(IS_APPLE)
+    return "2a308e8cc20a6221112c387d122075a8";
 #else
-#define MAYBE_PasswordAfterGenerateSave PasswordAfterGenerateSave
-#endif
-TEST_F(CPDFSecurityHandlerEmbedderTest, MAYBE_PasswordAfterGenerateSave) {
-#if _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
-  const char md5[] = "7048dca58e2ed8f93339008b91e4eb4e";
-#elif defined(OS_MACOSX)
-  const char md5[] = "6951b6c9891dfe0332a5b1983e484400";
-#else
-  const char md5[] = "041c2fb541c8907cc22ce101b686c79e";
-#endif  // _FX_PLATFORM_ == _FX_PLATFORM_LINUX_
+    return "9fe7eef8e51d15a604001854be6ed1ee";
+#endif  // BUILDFLAG(IS_APPLE)
+  }();
   {
     ASSERT_TRUE(OpenDocumentWithOptions("encrypted.pdf", "5678",
                                         LinearizeOption::kMustLinearize,
@@ -174,7 +156,7 @@
     EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
     FPDFPage_InsertObject(page, red_rect);
     ScopedFPDFBitmap bitmap = RenderLoadedPage(page);
-    CompareBitmap(bitmap.get(), 612, 792, md5);
+    CompareBitmap(bitmap.get(), 612, 792, checksum);
     EXPECT_TRUE(FPDFPage_GenerateContent(page));
     SetWholeFileAvailable();
     EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
@@ -196,8 +178,9 @@
   for (const auto& test : tests) {
     ASSERT_TRUE(OpenSavedDocumentWithPassword(test.password));
     FPDF_PAGE page = LoadSavedPage(0);
-    VerifySavedRendering(page, 612, 792, md5);
-    EXPECT_EQ(test.permissions, FPDF_GetDocPermissions(saved_document_));
+    ASSERT_TRUE(page);
+    VerifySavedRendering(page, 612, 792, checksum);
+    EXPECT_EQ(test.permissions, FPDF_GetDocPermissions(saved_document()));
 
     CloseSavedPage(page);
     CloseSavedDocument();
@@ -679,3 +662,7 @@
   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..e4bf0c0 100644
--- a/core/fpdfapi/parser/cpdf_simple_parser.h
+++ b/core/fpdfapi/parser/cpdf_simple_parser.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,8 +7,9 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_SIMPLE_PARSER_H_
 #define CORE_FPDFAPI_PARSER_CPDF_SIMPLE_PARSER_H_
 
-#include "core/fxcrt/fx_string.h"
-#include "core/fxcrt/fx_system.h"
+#include <stdint.h>
+
+#include "core/fxcrt/bytestring.h"
 #include "third_party/base/span.h"
 
 class CPDF_SimpleParser {
diff --git a/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp b/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp
index 5834d77..965d577 100644
--- a/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp
+++ b/core/fpdfapi/parser/cpdf_simple_parser_unittest.cpp
@@ -1,10 +1,10 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
 
-#include <string>
+#include <iterator>
 
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -50,7 +50,7 @@
       STR_IN_OUT_CASE(" $^&&*\t\0sdff ", "$^&&*"),
       STR_IN_OUT_CASE("\n\r+3.5656 -11.0", "+3.5656"),
   };
-  for (size_t i = 0; i < FX_ArraySize(test_data); ++i) {
+  for (size_t i = 0; i < std::size(test_data); ++i) {
     const pdfium::StrFuncTestData& data = test_data[i];
     CPDF_SimpleParser parser(pdfium::make_span(data.input, data.input_size));
     ByteStringView word = parser.GetWord();
diff --git a/core/fpdfapi/parser/cpdf_stream.cpp b/core/fpdfapi/parser/cpdf_stream.cpp
index 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..1f0e726 100644
--- a/core/fpdfapi/parser/cpdf_stream_acc.h
+++ b/core/fpdfapi/parser/cpdf_stream_acc.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,13 +7,14 @@
 #ifndef CORE_FPDFAPI_PARSER_CPDF_STREAM_ACC_H_
 #define CORE_FPDFAPI_PARSER_CPDF_STREAM_ACC_H_
 
+#include <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/abseil-cpp/absl/types/variant.h"
 #include "third_party/base/span.h"
 
 class CPDF_Dictionary;
@@ -21,8 +22,7 @@
 
 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..ed79af2 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/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..6f29b7a 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_decode.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,12 +6,11 @@
 
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 
+#include <ctype.h>
 #include <limits.h>
 
 #include <algorithm>
-#include <sstream>
 #include <utility>
-#include <vector>
 
 #include "constants/stream_dict_common.h"
 #include "core/fpdfapi/parser/cpdf_array.h"
@@ -19,12 +18,12 @@
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
 #include "core/fxcodec/fax/faxmodule.h"
 #include "core/fxcodec/flate/flatemodule.h"
-#include "core/fxcodec/fx_codec.h"
 #include "core/fxcodec/scanlinedecoder.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/stl_util.h"
+#include "core/fxcrt/span_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
 
 namespace {
 
@@ -42,7 +41,7 @@
   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 +56,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 +93,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 +107,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 +220,7 @@
       ++i;
       break;
     }
-    if (!std::isxdigit(ch))
+    if (!isxdigit(ch))
       continue;
 
     int digit = FXSYS_HexCharToInt(ch);
@@ -273,18 +274,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 +365,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 +413,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 +425,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 +442,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 +453,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;
@@ -506,14 +510,14 @@
   } 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 +526,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)
@@ -556,44 +560,44 @@
   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..a437455 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode.h
+++ b/core/fpdfapi/parser/fpdf_parser_decode.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,17 @@
 #ifndef CORE_FPDFAPI_PARSER_FPDF_PARSER_DECODE_H_
 #define CORE_FPDFAPI_PARSER_FPDF_PARSER_DECODE_H_
 
+#include <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 "core/fxcrt/retain_ptr.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/base/span.h"
 
 class CPDF_Array;
@@ -26,13 +29,14 @@
 }
 
 // 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..2c2074b 100644
--- a/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp
+++ b/core/fpdfapi/parser/fpdf_parser_decode_unittest.cpp
@@ -1,17 +1,22 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 
+#include <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/fx_memory_wrappers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/test_support.h"
 
-TEST(fpdf_parser_decode, ValidateDecoderPipeline) {
+TEST(ParserDecodeTest, ValidateDecoderPipeline) {
   {
     // Empty decoder list is always valid.
     auto decoders = pdfium::MakeRetain<CPDF_Array>();
@@ -20,99 +25,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 +274,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,7 +378,7 @@
   }
 }
 
-TEST(fpdf_parser_decode, DecodeText) {
+TEST(ParserDecodeTest, DecodeText) {
   const struct DecodeTestData {
     const char* input;
     size_t input_length;
@@ -231,7 +427,7 @@
   }
 }
 
-TEST(fpdf_parser_decode, EncodeText) {
+TEST(ParserDecodeTest, EncodeText) {
   const struct EncodeTestData {
     const wchar_t* input;
     const char* expected_output;
diff --git a/core/fpdfapi/parser/fpdf_parser_utility.cpp b/core/fpdfapi/parser/fpdf_parser_utility.cpp
index af58e8d..950753e 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,7 +20,8 @@
 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
 #include "core/fxcrt/fx_extension.h"
 #include "core/fxcrt/fx_stream.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/check.h"
+#include "third_party/base/notreached.h"
 
 // Indexed by 8-bit character code, contains either:
 //   'W' - for whitespace: NUL, TAB, CR, LF, FF, SPACE, 0x80, 0xff
@@ -72,29 +75,21 @@
     '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 +150,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 +182,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 +202,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 +217,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 +232,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,12 +244,12 @@
       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;
     }
diff --git a/core/fpdfapi/parser/fpdf_parser_utility.h b/core/fpdfapi/parser/fpdf_parser_utility.h
index 75f4076..259495e 100644
--- a/core/fpdfapi/parser/fpdf_parser_utility.h
+++ b/core/fpdfapi/parser/fpdf_parser_utility.h
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,12 +7,12 @@
 #ifndef CORE_FPDFAPI_PARSER_FPDF_PARSER_UTILITY_H_
 #define CORE_FPDFAPI_PARSER_FPDF_PARSER_UTILITY_H_
 
-#include <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;
@@ -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..e1a0d52
--- /dev/null
+++ b/core/fpdfapi/parser/object_tree_traversal_util.cpp
@@ -0,0 +1,221 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/parser/object_tree_traversal_util.h"
+
+#include <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 "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();
+          CHECK(referenced_object_number);
+
+          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) {
+            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;
+  }
+
+  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..c90c77e
--- /dev/null
+++ b/core/fpdfapi/parser/object_tree_traversal_util_embeddertest.cpp
@@ -0,0 +1,96 @@
+// Copyright 2023 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fpdfapi/parser/object_tree_traversal_util.h"
+
+#include <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,
+       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}));
+}
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..af0ea6d
--- /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/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..c5f3f1d 100644
--- a/core/fpdfapi/render/cpdf_imagerenderer.cpp
+++ b/core/fpdfapi/render/cpdf_imagerenderer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,38 +6,43 @@
 
 #include "core/fpdfapi/render/cpdf_imagerenderer.h"
 
+#include <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"
+#include "third_party/base/check.h"
+#include "third_party/base/cxx17_backports.h"
 
-#ifdef _SKIA_SUPPORT_
+#if defined(_SKIA_SUPPORT_)
 #include "core/fxge/skia/fx_skia_device.h"
 #endif
 
@@ -52,9 +57,34 @@
   return safe_val.ValueOrDefault(kLimit) >= kLimit;
 }
 
+void ClearBitmap(CFX_DefaultRenderDevice& bitmap_device, uint32_t color) {
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    bitmap_device.Clear(color);
+    return;
+  }
+#endif
+  bitmap_device.GetBitmap()->Clear(color);
+}
+
+RetainPtr<CFX_DIBBase> PreMultiplyBitmapIfAlpha(
+    RetainPtr<CFX_DIBBase> base_bitmap) {
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    RetainPtr<CFX_DIBitmap> premultiplied = base_bitmap->Realize();
+    if (base_bitmap->IsAlphaFormat())
+      premultiplied->PreMultiply();
+    return premultiplied;
+  }
+#endif  // defined(_SKIA_SUPPORT_)
+  return base_bitmap;
+}
+
 }  // namespace
 
-CPDF_ImageRenderer::CPDF_ImageRenderer() = 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 +92,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 +157,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 +176,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 +252,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 +261,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) {
@@ -277,10 +309,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 +325,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 +360,41 @@
 
   CFX_Matrix new_matrix = GetDrawMatrix(rect);
   CFX_DefaultRenderDevice bitmap_device1;
-  if (!bitmap_device1.Create(rect.Width(), rect.Height(), FXDIB_Rgb32, nullptr))
+  if (!bitmap_device1.Create(rect.Width(), rect.Height(), FXDIB_Format::kRgb32,
+                             nullptr)) {
     return true;
-
-#if defined _SKIA_SUPPORT_
-  bitmap_device1.Clear(0xffffff);
-#else
-  bitmap_device1.GetBitmap()->Clear(0xffffff);
-#endif
+  }
+  ClearBitmap(bitmap_device1, 0xffffffff);
   CPDF_RenderStatus bitmap_render(m_pRenderStatus->GetContext(),
                                   &bitmap_device1);
   bitmap_render.SetDropObjects(m_pRenderStatus->GetDropObjects());
   bitmap_render.SetStdCS(true);
   bitmap_render.Initialize(nullptr, nullptr);
-  CPDF_ImageRenderer image_render;
-  if (image_render.Start(&bitmap_render, m_pDIBBase, 0, 255, new_matrix,
-                         m_ResampleOptions, true, BlendMode::kNormal)) {
+  CPDF_ImageRenderer image_render(&bitmap_render);
+  if (image_render.Start(m_pDIBBase, 0, new_matrix, m_ResampleOptions, true)) {
     image_render.Continue(nullptr);
   }
   CFX_DefaultRenderDevice bitmap_device2;
-  if (!bitmap_device2.Create(rect.Width(), rect.Height(), FXDIB_8bppRgb,
-                             nullptr))
+  if (!bitmap_device2.Create(rect.Width(), rect.Height(),
+                             FXDIB_Format::k8bppRgb, nullptr)) {
     return true;
-
-#if defined _SKIA_SUPPORT_
-  bitmap_device2.Clear(0);
-#else
-  bitmap_device2.GetBitmap()->Clear(0);
-#endif
-  CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_Loader.GetMask(),
+  }
+  CalculateDrawImage(&bitmap_device1, &bitmap_device2, m_pLoader->GetMask(),
                      new_matrix, rect);
-#ifdef _SKIA_SUPPORT_
-  m_pRenderStatus->GetRenderDevice()->SetBitsWithMask(
-      bitmap_device1.GetBitmap(), bitmap_device2.GetBitmap(), rect.left,
-      rect.top, m_BitmapAlpha, m_BlendType);
-#else
-  bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_8bppMask);
+#if defined(_SKIA_SUPPORT_)
+  if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
+    m_pRenderStatus->GetRenderDevice()->SetBitsWithMask(
+        bitmap_device1.GetBitmap(), bitmap_device2.GetBitmap(), rect.left,
+        rect.top, m_BitmapAlpha, m_BlendType);
+    return false;
+  }
+#endif
+  bitmap_device2.GetBitmap()->ConvertFormat(FXDIB_Format::k8bppMask);
   bitmap_device1.GetBitmap()->MultiplyAlpha(bitmap_device2.GetBitmap());
   if (m_BitmapAlpha < 255)
     bitmap_device1.GetBitmap()->MultiplyAlpha(m_BitmapAlpha);
   m_pRenderStatus->GetRenderDevice()->SetDIBitsWithBlend(
       bitmap_device1.GetBitmap(), rect.left, rect.top, m_BlendType);
-#endif  //  _SKIA_SUPPORT_
   return false;
 }
 
@@ -388,30 +412,16 @@
       m_ResampleOptions.bInterpolateBilinear = true;
     }
   }
-#ifdef _SKIA_SUPPORT_
-  RetainPtr<CFX_DIBitmap> premultiplied = m_pDIBBase->Clone(nullptr);
-  if (m_pDIBBase->HasAlpha())
-    CFX_SkiaDeviceDriver::PreMultiply(premultiplied);
+  RetainPtr<CFX_DIBBase> bitmap = PreMultiplyBitmapIfAlpha(m_pDIBBase);
   if (m_pRenderStatus->GetRenderDevice()->StartDIBitsWithBlend(
-          premultiplied, m_BitmapAlpha, m_FillArgb, m_ImageMatrix,
-          m_ResampleOptions, &m_DeviceHandle, m_BlendType)) {
+          bitmap, m_BitmapAlpha, m_FillArgb, m_ImageMatrix, m_ResampleOptions,
+          &m_DeviceHandle, m_BlendType)) {
     if (m_DeviceHandle) {
       m_Mode = Mode::kBlend;
       return true;
     }
     return false;
   }
-#else
-  if (m_pRenderStatus->GetRenderDevice()->StartDIBitsWithBlend(
-          m_pDIBBase, m_BitmapAlpha, m_FillArgb, m_ImageMatrix,
-          m_ResampleOptions, &m_DeviceHandle, m_BlendType)) {
-    if (m_DeviceHandle) {
-      m_Mode = Mode::kBlend;
-      return true;
-    }
-    return false;
-  }
-#endif
 
   if ((fabs(m_ImageMatrix.b) >= 0.5f || m_ImageMatrix.a == 0) ||
       (fabs(m_ImageMatrix.c) >= 0.5f || m_ImageMatrix.d == 0)) {
@@ -420,19 +430,19 @@
       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 +462,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 +494,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 +524,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 +554,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 +582,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 +599,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 +625,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..67170fc 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,12 +44,12 @@
 
  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;
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..ce7543b 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/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
+#include "third_party/base/check.h"
+#include "third_party/base/check_op.h"
+#include "third_party/base/cxx17_backports.h"
+#include "third_party/base/span.h"
 
 namespace {
 
@@ -57,28 +65,28 @@
     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 = pdfium::clamp(min_x, 0, pBitmap->GetWidth());
+    int end_x = pdfium::clamp(max_x, 0, pBitmap->GetWidth());
     float r_unit = (r[end_index] - r[start_index]) / (max_x - min_x);
     float g_unit = (g[end_index] - g[start_index]) / (max_x - min_x);
     float b_unit = (b[end_index] - b[start_index]) / (max_x - min_x);
-    float R = r[start_index] + (start_x - min_x) * r_unit;
-    float G = g[start_index] + (start_x - min_x) * g_unit;
-    float B = b[start_index] + (start_x - min_x) * b_unit;
+    float r_result = r[start_index] + (start_x - min_x) * r_unit;
+    float g_result = g[start_index] + (start_x - min_x) * g_unit;
+    float b_result = b[start_index] + (start_x - min_x) * b_unit;
+    pdfium::span<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_Path path;
   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..fe58de8 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,34 +41,38 @@
 #include "core/fpdfapi/parser/cpdf_document.h"
 #include "core/fpdfapi/parser/cpdf_stream.h"
 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
-#include "core/fpdfapi/render/cpdf_charposlist.h"
+#include "core/fpdfapi/render/charposlist.h"
 #include "core/fpdfapi/render/cpdf_docrenderdata.h"
 #include "core/fpdfapi/render/cpdf_imagerenderer.h"
-#include "core/fpdfapi/render/cpdf_pagerendercache.h"
 #include "core/fpdfapi/render/cpdf_rendercontext.h"
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfapi/render/cpdf_rendershading.h"
+#include "core/fpdfapi/render/cpdf_rendertiling.h"
 #include "core/fpdfapi/render/cpdf_scaledrenderbuffer.h"
 #include "core/fpdfapi/render/cpdf_textrenderer.h"
 #include "core/fpdfapi/render/cpdf_type3cache.h"
 #include "core/fxcrt/autorestorer.h"
+#include "core/fxcrt/data_vector.h"
+#include "core/fxcrt/fx_2d_size.h"
 #include "core/fxcrt/fx_safe_types.h"
 #include "core/fxcrt/fx_system.h"
+#include "core/fxcrt/span_util.h"
+#include "core/fxcrt/unowned_ptr.h"
 #include "core/fxge/cfx_defaultrenderdevice.h"
+#include "core/fxge/cfx_fillrenderoptions.h"
 #include "core/fxge/cfx_glyphbitmap.h"
-#include "core/fxge/cfx_pathdata.h"
+#include "core/fxge/cfx_path.h"
 #include "core/fxge/dib/cfx_dibitmap.h"
 #include "core/fxge/fx_font.h"
 #include "core/fxge/renderdevicedriver_iface.h"
 #include "core/fxge/text_char_pos.h"
 #include "core/fxge/text_glyph_pos.h"
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/numerics/safe_math.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/span.h"
 
-#ifdef _SKIA_SUPPORT_
+#if defined(_SKIA_SUPPORT_)
 #include "core/fxge/skia/fx_skia_device.h"
 #endif
 
@@ -76,46 +81,55 @@
 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 +160,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 +196,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 +216,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 +233,6 @@
     return;
   }
   ProcessObjectNoClip(pObj, mtObj2Device);
-#if defined _SKIA_SUPPORT_
-  DebugVerifyDeviceIsPreMultiplied();
-#endif
 }
 
 bool CPDF_RenderStatus::ContinueSingleObject(CPDF_PageObject* pObj,
@@ -264,10 +249,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 +261,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 +282,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 +324,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 +347,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 +368,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 +430,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 +459,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 +489,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 +526,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 +587,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 +624,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 +649,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 +770,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 +809,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 +830,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 +845,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 +863,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 +879,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 +902,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 +919,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 +943,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 +1000,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 +1009,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 +1033,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 +1053,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 +1069,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 +1084,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 +1098,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,21 +1201,21 @@
     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);
+#if defined(_SKIA_SUPPORT_)
+      if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+        pDIBitmap->PreMultiply();
 #endif
       if (m_pDevice->SetDIBits(pDIBitmap, left, top)) {
         return;
@@ -1499,7 +1240,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 +1250,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 +1260,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 +1275,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->GetBuffer();
+  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 +1392,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 +1409,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 +1445,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..fcde2b2
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_rendertiling.cpp
@@ -0,0 +1,250 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "core/fpdfapi/render/cpdf_rendertiling.h"
+
+#include <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"
+
+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..b687f74
--- /dev/null
+++ b/core/fpdfapi/render/cpdf_rendertiling.h
@@ -0,0 +1,35 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#ifndef CORE_FPDFAPI_RENDER_CPDF_RENDERTILING_H_
+#define CORE_FPDFAPI_RENDER_CPDF_RENDERTILING_H_
+
+#include "core/fxcrt/retain_ptr.h"
+#include "core/fxge/dib/cfx_dibitmap.h"
+
+class CFX_Matrix;
+class CPDF_Form;
+class CPDF_PageObject;
+class CPDF_RenderStatus;
+class CPDF_TilingPattern;
+struct FX_RECT;
+
+class CPDF_RenderTiling {
+ public:
+  static RetainPtr<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..b5529a6 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/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..a51e16e 100644
--- a/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
+++ b/core/fpdfapi/render/fpdf_progressive_render_embeddertest.cpp
@@ -1,28 +1,81 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include <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 BUILDFLAG(IS_APPLE)
+  if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+    return "243f3d6267d9db09198fed9f8c4957fd";
+#endif
+  return "e31414933c9ff3950773981e5bf61678";
+}
+
+}  // namespace
 
 class FPDFProgressiveRenderEmbedderTest : public EmbedderTest {
  public:
+  class FakePause : public IFSDK_PAUSE {
+   public:
+    explicit FakePause(bool should_pause) : should_pause_(should_pause) {
+      IFSDK_PAUSE::version = 1;
+      IFSDK_PAUSE::user = nullptr;
+      IFSDK_PAUSE::NeedToPauseNow = Pause_NeedToPauseNow;
+    }
+    ~FakePause() = default;
+    static FPDF_BOOL Pause_NeedToPauseNow(IFSDK_PAUSE* param) {
+      return static_cast<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 +93,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 +152,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 +190,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 +200,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 +247,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 +266,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 +284,184 @@
   while (!render_done) {
     render_done = ContinueRenderPage(page, &pause);
   }
-  ScopedFPDFBitmap bitmap = FinishRenderPageWithForms(page, form_handle_);
-  CompareBitmap(bitmap.get(), 300, 300, kMd5ContentWithForms);
+  ScopedFPDFBitmap bitmap = FinishRenderPageWithForms(page, form_handle());
+  CompareBitmap(bitmap.get(), 300, 300, pdfium::TextFormChecksum());
   UnloadPage(page);
 }
+
+void FPDFProgressiveRenderEmbedderTest::VerifyRenderingWithColorScheme(
+    int page_num,
+    int flags,
+    const FPDF_COLORSCHEME* color_scheme,
+    FX_ARGB background_color,
+    int bitmap_width,
+    int bitmap_height,
+    const char* md5) {
+  ASSERT_TRUE(document());
+
+  FPDF_PAGE page = LoadPage(page_num);
+  ASSERT_TRUE(page);
+
+  ScopedFPDFBitmap bitmap = RenderPageWithForcedColorScheme(
+      page, form_handle(), flags, color_scheme, background_color);
+  ASSERT_TRUE(bitmap);
+  CompareBitmap(bitmap.get(), bitmap_width, bitmap_height, md5);
+  UnloadPage(page);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderTextWithColorScheme) {
+  // Test rendering of text with forced color scheme on.
+  const char* content_with_text_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "5ece6059efdc2ecb2894fa3cf329dc94";
+#if BUILDFLAG(IS_APPLE)
+    return "ee4ec12f54ce8d117a73bd9b85a8954d";
+#else
+    return "704db63ed2bf77254ecaa8035b85f21a";
+#endif  // BUILDFLAG(IS_APPLE)
+  }();
+
+  ASSERT_TRUE(OpenDocument("hello_world.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kBlack, kWhite, kWhite, kWhite};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, /*flags=*/0, &color_scheme,
+                                 kBlack, 200, 200, content_with_text_checksum);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderPathWithColorScheme) {
+  // Test rendering of paths with forced color scheme on.
+  const char* rectangles_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "4b0f850a94698d07b6cd2814d1b4ccb7";
+    return "249f59b0d066c4f6bd89782a80822219";
+  }();
+
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kWhite, kRed, kBlue, kBlue};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, /*flags=*/0, &color_scheme,
+                                 kBlack, 200, 300, rectangles_checksum);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest,
+       RenderPathWithColorSchemeAndConvertFillToStroke) {
+  // Test rendering of paths with forced color scheme on and conversion from
+  // fill to stroke enabled. The fill paths should be rendered as stroke.
+  const char* rectangles_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "c1cbbd2ce6921f608a3c55140592419b";
+    return "0ebcc11e617635eca1fa9ce475383a80";
+  }();
+
+  ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kWhite, kRed, kBlue, kBlue};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_CONVERT_FILL_TO_STROKE,
+                                 &color_scheme, kBlack, 200, 300,
+                                 rectangles_checksum);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderHighlightWithColorScheme) {
+  // Test rendering of highlight with forced color scheme on.
+  //
+  // Note: The fill color rendered for highlight is different from the normal
+  // path since highlights have Multiply blend mode, while the other path has
+  // Normal blend mode.
+  const char* content_with_highlight_fill_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "9b6273fdbc9db780c49f7540756209f8";
+#if BUILDFLAG(IS_APPLE)
+    return "a820afec9b99d3d3f2e9e9382bbad7c1";
+#else
+    return "a08a0639f89446f66f3689ee8e08b9fe";
+#endif  // BUILDFLAG(IS_APPLE)
+  }();
+
+  ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kRed, kGreen, kWhite, kWhite};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme,
+                                 kBlue, 612, 792,
+                                 content_with_highlight_fill_checksum);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest,
+       RenderHighlightWithColorSchemeAndConvertFillToStroke) {
+  // Test rendering of highlight with forced color and converting fill to
+  // stroke. The highlight should be rendered as a stroke of the rect.
+  //
+  // Note: The stroke color rendered for highlight is different from the normal
+  // path since highlights have Multiply blend mode, while the other path has
+  // Normal blend mode.
+
+  const char* md5_content_with_highlight = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "772246195d18f75d40a22bee913c098f";
+#if BUILDFLAG(IS_APPLE)
+    return "8837bea0b3520164b1784e513c882a2d";
+#else
+    return "3dd8c02f5c06bac85e0d2c8bf37d1dc4";
+#endif  // BUILDFLAG(IS_APPLE)
+  }();
+
+  ASSERT_TRUE(OpenDocument("annotation_highlight_square_with_ap.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kRed, kGreen, kWhite, kWhite};
+  VerifyRenderingWithColorScheme(
+      /*page_num=*/0, FPDF_ANNOT | FPDF_CONVERT_FILL_TO_STROKE, &color_scheme,
+      kBlue, 612, 792, md5_content_with_highlight);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderInkWithColorScheme) {
+  // Test rendering of multiple ink with forced color scheme on.
+  const char* content_with_ink_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "ebc57721e4c8da34156e09b9b2e62fb0";
+    return "797bce7dc6c50ee86b095405df9fe5aa";
+  }();
+
+  ASSERT_TRUE(OpenDocument("annotation_ink_multiple.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kBlack, kGreen, kRed, kRed};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme,
+                                 kBlack, 612, 792, content_with_ink_checksum);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderStampWithColorScheme) {
+  // Test rendering of static annotation with forced color scheme on.
+  const char* content_with_stamp_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "a791fdb4f595bb6c4187cc2aeed5e9e8";
+#if BUILDFLAG(IS_APPLE)
+    return "8170c539e95f22f14eb8f266a5f1bbed";
+#else
+    return "d1fd087e59d4dcebf47b56570bdb8c22";
+#endif
+  }();
+
+  ASSERT_TRUE(OpenDocument("annotation_stamp_with_ap.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kBlue, kGreen, kRed, kRed};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme,
+                                 kWhite, 595, 842, content_with_stamp_checksum);
+}
+
+TEST_F(FPDFProgressiveRenderEmbedderTest, RenderFormWithColorScheme) {
+  // Test rendering of form does not change with forced color scheme on.
+  const char* content_with_form_checksum = []() {
+    if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+      return "9f75d98afc6d6313bd87e6562ea6df15";
+    return "080f7a4381606659301440e1b14dca35";
+  }();
+
+  ASSERT_TRUE(OpenDocument("annotiter.pdf"));
+
+  FPDF_COLORSCHEME color_scheme{kGreen, kGreen, kRed, kRed};
+  VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT, &color_scheme,
+                                 kWhite, 612, 792, content_with_form_checksum);
+
+  // Verify that the MD5 hash matches when rendered without |color_scheme|.
+  VerifyRenderingWithColorScheme(/*page_num=*/0, FPDF_ANNOT,
+                                 /*color_scheme=*/nullptr, kWhite, 612, 792,
+                                 content_with_form_checksum);
+}
diff --git a/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp b/core/fpdfapi/render/fpdf_render_pattern_embeddertest.cpp
index 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..2e7a150 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,9 @@
 
 pdfium_unittest_source_set("unittests") {
   sources = [
+    "cpdf_action_unittest.cpp",
     "cpdf_annot_unittest.cpp",
+    "cpdf_bafontmap_unittest.cpp",
     "cpdf_defaultappearance_unittest.cpp",
     "cpdf_dest_unittest.cpp",
     "cpdf_filespec_unittest.cpp",
@@ -106,7 +107,13 @@
   ]
   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..f565a5a 100644
--- a/core/fpdfdoc/cpdf_annotlist.cpp
+++ b/core/fpdfdoc/cpdf_annotlist.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,10 +26,9 @@
 #include "core/fpdfapi/render/cpdf_renderoptions.h"
 #include "core/fpdfdoc/cpdf_annot.h"
 #include "core/fpdfdoc/cpdf_formfield.h"
+#include "core/fpdfdoc/cpdf_generateap.h"
 #include "core/fpdfdoc/cpdf_interactiveform.h"
-#include "core/fpdfdoc/cpvt_generateap.h"
 #include "core/fxge/cfx_renderdevice.h"
-#include "third_party/base/ptr_util.h"
 
 namespace {
 
@@ -49,6 +48,7 @@
     case CPDF_Annot::Subtype::CARET:
     case CPDF_Annot::Subtype::INK:
     case CPDF_Annot::Subtype::FILEATTACHMENT:
+    case CPDF_Annot::Subtype::REDACT:
       return true;
     case CPDF_Annot::Subtype::UNKNOWN:
     case CPDF_Annot::Subtype::LINK:
@@ -91,7 +91,7 @@
   pAnnotDict->SetNewFor<CPDF_Name>(pdfium::annotation::kSubtype, "Popup");
   pAnnotDict->SetNewFor<CPDF_String>(
       pdfium::form_fields::kT,
-      pParentDict->GetStringFor(pdfium::form_fields::kT), false);
+      pParentDict->GetByteStringFor(pdfium::form_fields::kT), false);
   pAnnotDict->SetNewFor<CPDF_String>(pdfium::annotation::kContents,
                                      sContents.ToUTF8(), false);
 
@@ -117,37 +117,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 +158,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 +221,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 +250,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 +264,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_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..1eda1d9 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,69 @@
     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;
 
+  float fc[4];
   CPDF_SimpleParser syntax(m_csDA.AsStringView().raw_span());
   if (FindTagParamFromStart(&syntax, "g", 1)) {
     fc[0] = StringToFloat(syntax.GetWord());
-    return {CFX_Color::kGray};
+    return CFX_Color(CFX_Color::Type::kGray, fc[0]);
   }
   if (FindTagParamFromStart(&syntax, "rg", 3)) {
     fc[0] = StringToFloat(syntax.GetWord());
     fc[1] = StringToFloat(syntax.GetWord());
     fc[2] = StringToFloat(syntax.GetWord());
-    return {CFX_Color::kRGB};
+    return CFX_Color(CFX_Color::Type::kRGB, fc[0], fc[1], fc[2]);
   }
   if (FindTagParamFromStart(&syntax, "k", 4)) {
     fc[0] = StringToFloat(syntax.GetWord());
     fc[1] = StringToFloat(syntax.GetWord());
     fc[2] = StringToFloat(syntax.GetWord());
     fc[3] = StringToFloat(syntax.GetWord());
-    return {CFX_Color::kCMYK};
+    return CFX_Color(CFX_Color::Type::kCMYK, fc[0], fc[1], fc[2], fc[3]);
   }
-
-  return {};
+  return absl::nullopt;
 }
 
-std::pair<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};
+  return absl::nullopt;
 }
 
+// static
 bool CPDF_DefaultAppearance::FindTagParamFromStartForTesting(
     CPDF_SimpleParser* parser,
     ByteStringView token,
diff --git a/core/fpdfdoc/cpdf_defaultappearance.h b/core/fpdfdoc/cpdf_defaultappearance.h
index 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..86bac27 100644
--- a/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp
+++ b/core/fpdfdoc/cpdf_defaultappearance_unittest.cpp
@@ -1,9 +1,12 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "core/fpdfdoc/cpdf_defaultappearance.h"
 
+#include <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"
@@ -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..8c6f25e
--- /dev/null
+++ b/core/fpdfdoc/cpdf_generateap.cpp
@@ -0,0 +1,1381 @@
+// Copyright 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) {
+      default:
+      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..5eb95e2f 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..3eb3c30 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/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..228a0c1 100644
--- a/core/fpdfdoc/cpdf_metadata.cpp
+++ b/core/fpdfdoc/cpdf_metadata.cpp
@@ -1,4 +1,4 @@
-// Copyright 2016 PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,14 +7,16 @@
 #include "core/fpdfdoc/cpdf_metadata.h"
 
 #include <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 {
 
@@ -60,17 +62,18 @@
 
 }  // 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)
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..cfcb8f3 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,7 +100,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());
@@ -116,7 +116,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 +146,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..3b60fab 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/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..b28774d 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(m_pDict);
 }
 
-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,47 +83,54 @@
 }
 
 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_Dictionary> pDict) {
+  RetainPtr<const CPDF_Object> pObj = pDict->GetObjectFor("Pg");
+  const CPDF_Reference* pRef = ToReference(pObj.Get());
+  const uint32_t PageObjNum = pRef ? pRef->GetRefObjNum() : 0;
+  RetainPtr<const CPDF_Object> pKids = pDict->GetDirectObjectFor("K");
   if (!pKids)
     return;
 
-  m_Kids.clear();
+  DCHECK(m_Kids.empty());
   if (const CPDF_Array* pArray = pKids->AsArray()) {
     m_Kids.resize(pArray->size());
-    for (uint32_t i = 0; i < pArray->size(); i++) {
-      const CPDF_Object* pKid = pArray->GetDirectObjectAt(i);
-      LoadKid(PageObjNum, pKid, &m_Kids[i]);
+    for (size_t i = 0; i < pArray->size(); ++i) {
+      LoadKid(PageObjNum, pArray->GetDirectObjectAt(i), &m_Kids[i]);
     }
     return;
   }
 
   m_Kids.resize(1);
-  LoadKid(PageObjNum, pKids, &m_Kids[0]);
+  LoadKid(PageObjNum, std::move(pKids), &m_Kids[0]);
 }
 
 void CPDF_StructElement::LoadKid(uint32_t PageObjNum,
-                                 const CPDF_Object* pKidObj,
-                                 CPDF_StructKid* pKid) {
-  pKid->m_Type = CPDF_StructKid::kInvalid;
+                                 RetainPtr<const CPDF_Object> pKidObj,
+                                 Kid* pKid) {
   if (!pKidObj)
     return;
 
   if (pKidObj->IsNumber()) {
-    if (m_pTree->GetPage()->GetObjNum() != PageObjNum)
+    if (m_pTree->GetPageObjNum() != PageObjNum)
       return;
 
-    pKid->m_Type = CPDF_StructKid::kPageContent;
+    pKid->m_Type = Kid::kPageContent;
     pKid->m_ContentId = pKidObj->GetInteger();
     pKid->m_PageObjNum = PageObjNum;
     return;
@@ -112,18 +139,21 @@
   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"))) {
+    PageObjNum = pRef->GetRefObjNum();
+  }
+  ByteString type = pKidDict->GetNameFor("Type");
   if ((type == "MCR" || type == "OBJR") &&
-      m_pTree->GetPage()->GetObjNum() != PageObjNum) {
+      m_pTree->GetPageObjNum() != PageObjNum) {
     return;
   }
 
   if (type == "MCR") {
-    pKid->m_Type = CPDF_StructKid::kStreamContent;
-    const CPDF_Reference* pRef = ToReference(pKidDict->GetObjectFor("Stm"));
+    pKid->m_Type = Kid::kStreamContent;
+    RetainPtr<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");
@@ -131,14 +161,14 @@
   }
 
   if (type == "OBJR") {
-    pKid->m_Type = CPDF_StructKid::kObject;
-    const CPDF_Reference* pObj = ToReference(pKidDict->GetObjectFor("Obj"));
+    pKid->m_Type = Kid::kObject;
+    RetainPtr<const CPDF_Reference> pObj =
+        ToReference(pKidDict->GetObjectFor("Obj"));
     pKid->m_RefObjNum = pObj ? pObj->GetRefObjNum() : 0;
     pKid->m_PageObjNum = PageObjNum;
     return;
   }
 
-  pKid->m_Type = CPDF_StructKid::kElement;
+  pKid->m_Type = Kid::kElement;
   pKid->m_pDict.Reset(pKidDict);
-  pKid->m_pElement = nullptr;
 }
diff --git a/core/fpdfdoc/cpdf_structelement.h b/core/fpdfdoc/cpdf_structelement.h
index 54042a9..6dd8f8f 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 LoadKids(RetainPtr<const CPDF_Dictionary> pDict);
   void LoadKid(uint32_t PageObjNum,
-               const CPDF_Object* pKidObj,
-               CPDF_StructKid* pKid);
+               RetainPtr<const CPDF_Object> pKidObj,
+               Kid* pKid);
 
-  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..67ac948 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,13 @@
 #include "core/fpdfapi/parser/cpdf_reference.h"
 #include "core/fpdfdoc/cpdf_numbertree.h"
 #include "core/fpdfdoc/cpdf_structelement.h"
-#include "third_party/base/ptr_util.h"
+#include "core/fxcrt/stl_util.h"
 
 namespace {
 
 bool IsTagged(const CPDF_Document* pDoc) {
-  const CPDF_Dictionary* pCatalog = pDoc->GetRoot();
-  const CPDF_Dictionary* pMarkInfo = pCatalog->GetDictFor("MarkInfo");
+  RetainPtr<const CPDF_Dictionary> pMarkInfo =
+      pDoc->GetRoot()->GetDictFor("MarkInfo");
   return pMarkInfo && pMarkInfo->GetIntegerFor("Marked");
 }
 
@@ -28,12 +30,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,12 +45,21 @@
 
 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;
 
@@ -56,34 +67,38 @@
   if (pKids->IsDictionary())
     dwKids = 1;
   else if (const CPDF_Array* pArray = pKids->AsArray())
-    dwKids = pArray->size();
+    dwKids = fxcrt::CollectionSize<uint32_t>(*pArray);
   else
     return;
 
   m_Kids.clear();
   m_Kids.resize(dwKids);
-  const CPDF_Dictionary* pParentTree = m_pTreeRoot->GetDictFor("ParentTree");
+
+  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 +109,33 @@
   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;
-    }
-  }
-  if (!bSave)
-    map->erase(pDict);
+      AddPageNode(std::move(pParent), map, nLevel + 1);
+  if (!pParentElement)
+    return pElement;
+
+  if (!pParentElement->UpdateKidIfElement(pDict, pElement.Get()))
+    map->erase(key);
+
+  pElement->SetParent(pParentElement.Get());
+
   return pElement;
 }
 
 bool CPDF_StructTree::AddTopLevelNode(
     const CPDF_Dictionary* pDict,
     const RetainPtr<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..d26655f 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,7 +75,7 @@
 }
 
 int32_t CPVT_FontMap::GetWordFontIndex(uint16_t word,
-                                       int32_t charset,
+                                       FX_Charset charset,
                                        int32_t nFontIndex) {
   NOTREACHED();
   return 0;
@@ -91,7 +86,8 @@
   return 0;
 }
 
-int32_t CPVT_FontMap::CharSetFromUnicode(uint16_t word, int32_t nOldCharset) {
+FX_Charset CPVT_FontMap::CharSetFromUnicode(uint16_t word,
+                                            FX_Charset nOldCharset) {
   NOTREACHED();
-  return FX_CHARSET_ANSI;
+  return FX_Charset::kANSI;
 }
diff --git a/core/fpdfdoc/cpvt_fontmap.h b/core/fpdfdoc/cpvt_fontmap.h
index 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";